806 lines
27 KiB
Go
806 lines
27 KiB
Go
//go:build llm_script
|
|
|
|
package main
|
|
|
|
import (
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
func sampleReportForV1() *ReportV3 {
|
|
return &ReportV3{
|
|
Date: "2026-05-13",
|
|
GeneratedAt: "2026-05-13T09:30:00+08:00",
|
|
TotalModels: 504,
|
|
AllModels: []ModelInfo{
|
|
{
|
|
Name: "DeepSeek-V4-Flash",
|
|
ProviderName: "DeepSeek",
|
|
ProviderCountry: "CN",
|
|
ContextLength: 262144,
|
|
InputPrice: 0.30,
|
|
OutputPrice: 1.20,
|
|
Currency: "USD",
|
|
OperatorName: "OpenRouter",
|
|
OperatorType: "reseller",
|
|
Region: "global",
|
|
SceneTags: []SceneTag{SceneCode, SceneReasoning},
|
|
},
|
|
{
|
|
Name: "glm-5",
|
|
ProviderName: "Zhipu",
|
|
ProviderCountry: "CN",
|
|
ContextLength: 131072,
|
|
InputPrice: 0,
|
|
OutputPrice: 0,
|
|
Currency: "CNY",
|
|
IsFree: true,
|
|
OperatorName: "Zhipu",
|
|
OperatorType: "official",
|
|
Region: "cn",
|
|
SceneTags: []SceneTag{SceneWriting, SceneChat},
|
|
},
|
|
{
|
|
Name: "claude-3.7-sonnet",
|
|
ProviderName: "Anthropic",
|
|
ProviderCountry: "US",
|
|
ContextLength: 200000,
|
|
InputPrice: 3.0,
|
|
OutputPrice: 15.0,
|
|
Currency: "USD",
|
|
OperatorName: "Anthropic",
|
|
OperatorType: "official",
|
|
Region: "global",
|
|
SceneTags: []SceneTag{SceneWriting, SceneChat},
|
|
},
|
|
{
|
|
Name: "qwen-vl-max",
|
|
ProviderName: "Alibaba",
|
|
ProviderCountry: "CN",
|
|
ContextLength: 65536,
|
|
InputPrice: 0.8,
|
|
OutputPrice: 2.4,
|
|
Currency: "CNY",
|
|
OperatorName: "DashScope",
|
|
OperatorType: "cloud",
|
|
Region: "cn",
|
|
SceneTags: []SceneTag{SceneVision},
|
|
},
|
|
},
|
|
FreeModels: []ModelInfo{
|
|
{
|
|
Name: "glm-5",
|
|
ProviderName: "Zhipu",
|
|
ProviderCountry: "CN",
|
|
ContextLength: 131072,
|
|
Currency: "CNY",
|
|
IsFree: true,
|
|
OperatorName: "Zhipu",
|
|
OperatorType: "official",
|
|
Region: "cn",
|
|
SceneTags: []SceneTag{SceneWriting, SceneChat},
|
|
},
|
|
{
|
|
Name: "DeepSeek-V4-Flash",
|
|
ProviderName: "DeepSeek",
|
|
ProviderCountry: "CN",
|
|
ContextLength: 262144,
|
|
Currency: "USD",
|
|
IsFree: true,
|
|
OperatorName: "OpenRouter",
|
|
OperatorType: "reseller",
|
|
Region: "global",
|
|
SceneTags: []SceneTag{SceneCode, SceneReasoning},
|
|
},
|
|
{
|
|
Name: "mystery-free-model",
|
|
ProviderName: "Unknown",
|
|
ProviderCountry: "unknown",
|
|
ContextLength: 65536,
|
|
Currency: "USD",
|
|
IsFree: true,
|
|
OperatorName: "Unknown Gateway",
|
|
OperatorType: "self_hosted_gateway",
|
|
Region: "global",
|
|
SceneTags: []SceneTag{SceneChat},
|
|
},
|
|
},
|
|
FreeTop20: []ModelInfo{
|
|
{
|
|
Name: "glm-5",
|
|
ProviderName: "Zhipu",
|
|
ProviderCountry: "CN",
|
|
ContextLength: 131072,
|
|
Currency: "CNY",
|
|
IsFree: true,
|
|
OperatorName: "Zhipu",
|
|
OperatorType: "official",
|
|
Region: "cn",
|
|
},
|
|
},
|
|
IntlTop5: []ModelInfo{
|
|
{
|
|
Name: "DeepSeek-V4-Flash",
|
|
ProviderName: "DeepSeek",
|
|
ProviderCountry: "CN",
|
|
ContextLength: 262144,
|
|
InputPrice: 0.30,
|
|
OutputPrice: 1.20,
|
|
Currency: "USD",
|
|
OperatorName: "OpenRouter",
|
|
OperatorType: "reseller",
|
|
Region: "global",
|
|
SceneTags: []SceneTag{SceneCode, SceneReasoning},
|
|
},
|
|
},
|
|
DomesticTop10: []ModelInfo{
|
|
{
|
|
Name: "qwen-vl-max",
|
|
ProviderName: "Alibaba",
|
|
ProviderCountry: "CN",
|
|
ContextLength: 65536,
|
|
InputPrice: 0.8,
|
|
OutputPrice: 2.4,
|
|
Currency: "CNY",
|
|
OperatorName: "DashScope",
|
|
OperatorType: "cloud",
|
|
Region: "cn",
|
|
SceneTags: []SceneTag{SceneVision},
|
|
},
|
|
},
|
|
DailySignals: DailySignals{
|
|
NewModels: 2,
|
|
PriceChanges: 1,
|
|
OfficialFree: 1,
|
|
AggregatorFree: 1,
|
|
UnknownFree: 1,
|
|
},
|
|
}
|
|
}
|
|
|
|
func TestBuildFreeSourceBreakdown(t *testing.T) {
|
|
report := sampleReportForV1()
|
|
|
|
breakdown := buildFreeSourceBreakdown(report.FreeModels)
|
|
|
|
if len(breakdown) != 3 {
|
|
t.Fatalf("expected 3 free source groups, got %d", len(breakdown))
|
|
}
|
|
|
|
if breakdown[0].Label != "官方免费" || breakdown[0].Count != 1 {
|
|
t.Fatalf("unexpected official free breakdown: %+v", breakdown[0])
|
|
}
|
|
if breakdown[1].Label != "聚合免费" || breakdown[1].Count != 1 {
|
|
t.Fatalf("unexpected aggregator free breakdown: %+v", breakdown[1])
|
|
}
|
|
if breakdown[2].Label != "待确认" || breakdown[2].Count != 1 {
|
|
t.Fatalf("unexpected unknown free breakdown: %+v", breakdown[2])
|
|
}
|
|
}
|
|
|
|
func TestResolveReportDateDefaultsToToday(t *testing.T) {
|
|
got, err := resolveReportDate(time.Date(2026, 5, 14, 9, 0, 0, 0, time.FixedZone("CST", 8*3600)), nil, "")
|
|
if err != nil {
|
|
t.Fatalf("resolveReportDate returned error: %v", err)
|
|
}
|
|
if got != "2026-05-14" {
|
|
t.Fatalf("date = %q, want %q", got, "2026-05-14")
|
|
}
|
|
}
|
|
|
|
func TestResolveReportDateUsesEnvDate(t *testing.T) {
|
|
got, err := resolveReportDate(time.Now(), nil, "2026-05-09")
|
|
if err != nil {
|
|
t.Fatalf("resolveReportDate returned error: %v", err)
|
|
}
|
|
if got != "2026-05-09" {
|
|
t.Fatalf("date = %q, want %q", got, "2026-05-09")
|
|
}
|
|
}
|
|
|
|
func TestResolveReportDateCLIOverridesEnv(t *testing.T) {
|
|
got, err := resolveReportDate(time.Now(), []string{"-date", "2024-06-05"}, "2026-05-09")
|
|
if err != nil {
|
|
t.Fatalf("resolveReportDate returned error: %v", err)
|
|
}
|
|
if got != "2024-06-05" {
|
|
t.Fatalf("date = %q, want %q", got, "2024-06-05")
|
|
}
|
|
}
|
|
|
|
func TestResolveReportDateSupportsEqualsSyntax(t *testing.T) {
|
|
got, err := resolveReportDate(time.Now(), []string{"--date=2024-10-25"}, "")
|
|
if err != nil {
|
|
t.Fatalf("resolveReportDate returned error: %v", err)
|
|
}
|
|
if got != "2024-10-25" {
|
|
t.Fatalf("date = %q, want %q", got, "2024-10-25")
|
|
}
|
|
}
|
|
|
|
func TestResolveReportDateRejectsInvalidDate(t *testing.T) {
|
|
if _, err := resolveReportDate(time.Now(), []string{"-date", "2026/05/09"}, ""); err == nil {
|
|
t.Fatalf("expected invalid date error")
|
|
}
|
|
}
|
|
|
|
func TestDecorateReportV1BuildsHotDaySummary(t *testing.T) {
|
|
report := sampleReportForV1()
|
|
report.ModelEvents = []ModelEvent{
|
|
{
|
|
EventType: "official_release",
|
|
ModelName: "GLM-5",
|
|
ProviderName: "Zhipu",
|
|
OperatorName: "Zhipu",
|
|
TrustLabel: "官方来源 / 一级证据",
|
|
Baseline: "官方首次发布",
|
|
Summary: "官方发布新模型,值得优先复查中文通用与推理场景默认选择。",
|
|
SourceKindLabel: "一级官方发布",
|
|
PrimarySource: "https://open.bigmodel.cn/dev/howuse/model",
|
|
UpdatedAt: "2026-05-13 08:30",
|
|
EvidenceDetail: "models.release_date = 今日,且 source_url 指向官方文档",
|
|
Priority: 120,
|
|
},
|
|
{
|
|
EventType: "official_release",
|
|
ModelName: "Doubao Seed 1.8",
|
|
ProviderName: "ByteDance",
|
|
OperatorName: "ByteDance Volcano",
|
|
TrustLabel: "官方来源 / 二级佐证",
|
|
Baseline: "官方首次发布",
|
|
Summary: "次级权威报道已形成稳定发布日期信号,适合进入观察池但不应与一级公告混同。",
|
|
SourceKindLabel: "二级权威佐证发布",
|
|
PrimarySource: "https://developer.volcengine.com/articles/7601918680544641034",
|
|
UpdatedAt: "2025-12-18 00:00",
|
|
EvidenceDetail: "models.release_date = 今日,发布日期采用次级权威报道佐证,模型来源页保留官方文档",
|
|
Priority: 110,
|
|
},
|
|
{
|
|
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: "价格下降已足以影响视觉模型默认选择。",
|
|
SourceKindLabel: "价格快照",
|
|
PrimarySource: "pricing_history",
|
|
UpdatedAt: "2026-05-13 10:00",
|
|
EvidenceDetail: "pricing_history 记录到输入价格较昨日下降 18%",
|
|
PriceChangePct: -18,
|
|
Priority: 90,
|
|
},
|
|
}
|
|
|
|
decorateReportV1(report)
|
|
|
|
if report.PageMode != "hot" {
|
|
t.Fatalf("expected hot page mode, got %q", report.PageMode)
|
|
}
|
|
if !strings.Contains(report.HeroSummary, "GLM-5 已出现官方发布信号") {
|
|
t.Fatalf("hero summary missing official release signal: %s", report.HeroSummary)
|
|
}
|
|
if len(report.ActionItems) != 3 {
|
|
t.Fatalf("expected 3 action items, got %d", len(report.ActionItems))
|
|
}
|
|
if len(report.HeadlineItems) == 0 {
|
|
t.Fatalf("expected headline items to be built")
|
|
}
|
|
if report.ActionItems[0].Evidence == "" {
|
|
t.Fatalf("expected action item evidence to be populated")
|
|
}
|
|
if !strings.Contains(report.HeadlineItems[0].Title, "GLM-5") {
|
|
t.Fatalf("expected first headline to prioritize official release, got %+v", report.HeadlineItems[0])
|
|
}
|
|
}
|
|
|
|
func TestDecorateReportV1BuildsCalmDaySummary(t *testing.T) {
|
|
report := sampleReportForV1()
|
|
report.DailySignals = DailySignals{}
|
|
|
|
decorateReportV1(report)
|
|
|
|
if report.PageMode != "calm" {
|
|
t.Fatalf("expected calm page mode, got %q", report.PageMode)
|
|
}
|
|
if !strings.Contains(report.HeroSummary, "稳定") {
|
|
t.Fatalf("expected calm day summary to emphasize stability, got %s", report.HeroSummary)
|
|
}
|
|
}
|
|
|
|
func TestGenerateMarkdownV3IncludesTencentSubscriptionSection(t *testing.T) {
|
|
path := filepath.Join(t.TempDir(), "daily_report.md")
|
|
report := sampleReportForV1()
|
|
report.QualitySummary = DataQualitySummary{
|
|
Total: 502,
|
|
Fresh: 490,
|
|
CNY: 126,
|
|
USD: 376,
|
|
}
|
|
report.TencentSubscriptionPlans = []SubscriptionPlanInfo{
|
|
{
|
|
PlanName: "通用 Token Plan Lite",
|
|
PlanFamily: "token_plan",
|
|
Tier: "Lite",
|
|
Currency: "CNY",
|
|
ListPrice: 39,
|
|
QuotaValue: 35000000,
|
|
QuotaUnit: "tokens/month",
|
|
ContextWindow: 0,
|
|
ModelCount: 10,
|
|
ModelPreview: "tc-code-latest, glm-5, glm-5.1",
|
|
},
|
|
{
|
|
PlanName: "Hy Token Plan Max",
|
|
PlanFamily: "token_plan",
|
|
Tier: "Max",
|
|
Currency: "CNY",
|
|
ListPrice: 468,
|
|
QuotaValue: 650000000,
|
|
QuotaUnit: "tokens/month",
|
|
ContextWindow: 262144,
|
|
ModelCount: 1,
|
|
ModelPreview: "hy3-preview",
|
|
},
|
|
}
|
|
report.ModelEvents = []ModelEvent{
|
|
{
|
|
EventType: "official_release",
|
|
ModelName: "GLM-5",
|
|
ProviderName: "Zhipu",
|
|
OperatorName: "Zhipu",
|
|
TrustLabel: "官方来源 / 一级证据",
|
|
Baseline: "官方首次发布",
|
|
Summary: "官方发布新模型,值得优先复查中文通用与推理场景默认选择。",
|
|
SourceKindLabel: "一级官方发布",
|
|
PrimarySource: "https://open.bigmodel.cn/dev/howuse/model",
|
|
UpdatedAt: "2026-05-13 08:30",
|
|
EvidenceDetail: "models.release_date = 今日,且 source_url 指向官方文档",
|
|
Priority: 120,
|
|
},
|
|
{
|
|
EventType: "official_release",
|
|
ModelName: "Doubao Seed 1.8",
|
|
ProviderName: "ByteDance",
|
|
OperatorName: "ByteDance Volcano",
|
|
TrustLabel: "官方来源 / 二级佐证",
|
|
Baseline: "官方首次发布",
|
|
Summary: "次级权威报道已形成稳定发布日期信号,适合进入观察池但不应与一级公告混同。",
|
|
SourceKindLabel: "二级权威佐证发布",
|
|
PrimarySource: "https://developer.volcengine.com/articles/7601918680544641034",
|
|
UpdatedAt: "2025-12-18 00:00",
|
|
EvidenceDetail: "models.release_date = 今日,发布日期采用次级权威报道佐证,模型来源页保留官方文档",
|
|
Priority: 110,
|
|
},
|
|
{
|
|
EventType: "new_model",
|
|
ModelName: "DeepSeek-V4-Flash",
|
|
ProviderName: "DeepSeek",
|
|
OperatorName: "OpenRouter",
|
|
Audience: "适合想尽快验证新模型价值的选型读者",
|
|
TrustLabel: "聚合来源",
|
|
Baseline: "首次出现",
|
|
Summary: "新模型进入情报池,值得重新评估低成本编码默认选择。",
|
|
SourceKindLabel: "模型快照",
|
|
PrimarySource: "OpenRouter / region_pricing",
|
|
UpdatedAt: "2026-05-13 09:30",
|
|
EvidenceDetail: "models.created_at = 今日,且已存在最新价格快照",
|
|
Priority: 95,
|
|
},
|
|
{
|
|
EventType: "promo_campaign",
|
|
ModelName: "DeepSeek-V3.2-Exp",
|
|
ProviderName: "DeepSeek",
|
|
OperatorName: "DeepSeek",
|
|
Audience: "适合计划趁活动窗口压低推理成本的团队",
|
|
TrustLabel: "官方来源 / 一级证据",
|
|
Baseline: "活动窗口开启",
|
|
Summary: "官方活动窗口出现后,值得重新评估低成本推理和批量调用方案。",
|
|
SourceKindLabel: "官方活动页",
|
|
PrimarySource: "https://api-docs.deepseek.com/news/news250929",
|
|
UpdatedAt: "2025-09-29 00:00",
|
|
EvidenceDetail: "官方活动页记录 V3.2-Exp 在活动窗口内价格下调 50%+",
|
|
Priority: 115,
|
|
},
|
|
}
|
|
decorateReportV1(report)
|
|
|
|
if err := generateMarkdownV3(report, path); err != nil {
|
|
t.Fatalf("generateMarkdownV3 returned error: %v", err)
|
|
}
|
|
|
|
body, err := os.ReadFile(path)
|
|
if err != nil {
|
|
t.Fatalf("read markdown output: %v", err)
|
|
}
|
|
|
|
content := string(body)
|
|
for _, want := range []string{
|
|
"## 今日结论",
|
|
"## 今日行动建议",
|
|
"## 今日变化",
|
|
"## 场景推荐",
|
|
"## 完整数据附录",
|
|
"- 影响对象:",
|
|
"营销活动",
|
|
"主来源: OpenRouter / region_pricing",
|
|
"更新时间: 2026-05-13 09:30",
|
|
"判定依据: models.created_at = 今日,且已存在最新价格快照",
|
|
"## 💳 腾讯云套餐订阅价",
|
|
"通用 Token Plan Lite",
|
|
"Hy Token Plan Max",
|
|
"¥39.00/月",
|
|
"3500万 Tokens/月",
|
|
"256K",
|
|
} {
|
|
if !strings.Contains(content, want) {
|
|
t.Fatalf("markdown missing %q\n%s", want, content)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestGenerateHTMLV3IncludesTencentSubscriptionSection(t *testing.T) {
|
|
path := filepath.Join(t.TempDir(), "daily_report.html")
|
|
report := sampleReportForV1()
|
|
report.ModelEvents = []ModelEvent{
|
|
{
|
|
EventType: "official_release",
|
|
ModelName: "GLM-5",
|
|
ProviderName: "Zhipu",
|
|
OperatorName: "Zhipu",
|
|
TrustLabel: "官方来源 / 一级证据",
|
|
Baseline: "官方首次发布",
|
|
Summary: "官方发布新模型,值得优先复查中文通用与推理场景默认选择。",
|
|
SourceKindLabel: "一级官方发布",
|
|
PrimarySource: "https://open.bigmodel.cn/dev/howuse/model",
|
|
UpdatedAt: "2026-05-13 08:30",
|
|
EvidenceDetail: "models.release_date = 今日,且 source_url 指向官方文档",
|
|
Priority: 120,
|
|
},
|
|
{
|
|
EventType: "official_release",
|
|
ModelName: "Doubao Seed 1.8",
|
|
ProviderName: "ByteDance",
|
|
OperatorName: "ByteDance Volcano",
|
|
TrustLabel: "官方来源 / 二级佐证",
|
|
Baseline: "官方首次发布",
|
|
Summary: "次级权威报道已形成稳定发布日期信号,适合进入观察池但不应与一级公告混同。",
|
|
SourceKindLabel: "二级权威佐证发布",
|
|
PrimarySource: "https://developer.volcengine.com/articles/7601918680544641034",
|
|
UpdatedAt: "2025-12-18 00:00",
|
|
EvidenceDetail: "models.release_date = 今日,发布日期采用次级权威报道佐证,模型来源页保留官方文档",
|
|
Priority: 110,
|
|
},
|
|
{
|
|
EventType: "new_model",
|
|
ModelName: "DeepSeek-V4-Flash",
|
|
ProviderName: "DeepSeek",
|
|
OperatorName: "OpenRouter",
|
|
Audience: "适合想尽快验证新模型价值的选型读者",
|
|
TrustLabel: "聚合来源",
|
|
Baseline: "首次出现",
|
|
Summary: "新模型进入情报池,值得重新评估低成本编码默认选择。",
|
|
SourceKindLabel: "模型快照",
|
|
PrimarySource: "OpenRouter / region_pricing",
|
|
UpdatedAt: "2026-05-13 09:30",
|
|
EvidenceDetail: "models.created_at = 今日,且已存在最新价格快照",
|
|
Priority: 95,
|
|
},
|
|
{
|
|
EventType: "promo_campaign",
|
|
ModelName: "DeepSeek-V3.2-Exp",
|
|
ProviderName: "DeepSeek",
|
|
OperatorName: "DeepSeek",
|
|
Audience: "适合计划趁活动窗口压低推理成本的团队",
|
|
TrustLabel: "官方来源 / 一级证据",
|
|
Baseline: "活动窗口开启",
|
|
Summary: "官方活动窗口出现后,值得重新评估低成本推理和批量调用方案。",
|
|
SourceKindLabel: "官方活动页",
|
|
PrimarySource: "https://api-docs.deepseek.com/news/news250929",
|
|
UpdatedAt: "2025-09-29 00:00",
|
|
EvidenceDetail: "官方活动页记录 V3.2-Exp 在活动窗口内价格下调 50%+",
|
|
Priority: 115,
|
|
},
|
|
}
|
|
report.TencentSubscriptionPlans = []SubscriptionPlanInfo{
|
|
{
|
|
PlanName: "通用 Token Plan Lite",
|
|
PlanFamily: "token_plan",
|
|
Tier: "Lite",
|
|
Currency: "CNY",
|
|
ListPrice: 39,
|
|
QuotaValue: 35000000,
|
|
QuotaUnit: "tokens/month",
|
|
ModelCount: 10,
|
|
ModelPreview: "tc-code-latest, glm-5, glm-5.1",
|
|
},
|
|
}
|
|
decorateReportV1(report)
|
|
|
|
if err := generateHTMLV3(report, path); err != nil {
|
|
t.Fatalf("generateHTMLV3 returned error: %v", err)
|
|
}
|
|
|
|
body, err := os.ReadFile(path)
|
|
if err != nil {
|
|
t.Fatalf("read html output: %v", err)
|
|
}
|
|
|
|
content := string(body)
|
|
for _, want := range []string{
|
|
"今日一句话结论",
|
|
"三条行动建议",
|
|
"今日头条",
|
|
"DeepSeek-V4-Flash",
|
|
"一级官方发布",
|
|
"二级权威佐证",
|
|
"营销活动",
|
|
"影响对象",
|
|
"首次出现",
|
|
"主来源",
|
|
"更新时间",
|
|
"判定依据",
|
|
"模型快照",
|
|
"场景推荐",
|
|
"完整数据附录",
|
|
"官方免费",
|
|
"聚合免费",
|
|
"待确认",
|
|
"💳 腾讯云套餐订阅价",
|
|
} {
|
|
if !strings.Contains(content, want) {
|
|
t.Fatalf("html missing %q\n%s", want, content)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestBuildHeadlineItemsUsesModelEvents(t *testing.T) {
|
|
report := sampleReportForV1()
|
|
report.ModelEvents = []ModelEvent{
|
|
{
|
|
EventType: "official_release",
|
|
ModelName: "GLM-5",
|
|
ProviderName: "Zhipu",
|
|
OperatorName: "Zhipu",
|
|
TrustLabel: "官方来源",
|
|
Baseline: "官方首次发布",
|
|
Summary: "官方发布新模型,值得优先复查中文通用与推理场景默认选择。",
|
|
SourceKindLabel: "一级官方发布",
|
|
PrimarySource: "https://open.bigmodel.cn/dev/howuse/model",
|
|
UpdatedAt: "2026-05-13 08:30",
|
|
EvidenceDetail: "models.release_date = 今日,且 source_url 指向官方文档",
|
|
Priority: 120,
|
|
},
|
|
{
|
|
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: "新模型进入情报池,值得重新评估低成本编码默认选择。",
|
|
SourceKindLabel: "模型快照",
|
|
PrimarySource: "OpenRouter / region_pricing",
|
|
UpdatedAt: "2026-05-13 09:30",
|
|
EvidenceDetail: "models.created_at = 今日,且已存在最新价格快照",
|
|
Priority: 90,
|
|
},
|
|
}
|
|
|
|
items := buildHeadlineItems(report)
|
|
|
|
if len(items) < 2 {
|
|
t.Fatalf("expected at least 2 headline items, got %d", len(items))
|
|
}
|
|
if !strings.Contains(items[0].Title, "GLM-5") || items[0].Label != "一级官方发布" {
|
|
t.Fatalf("expected official release event to rank first, got %+v", items[0])
|
|
}
|
|
if items[1].Baseline != "较昨日 -25%" {
|
|
t.Fatalf("expected price_cut baseline to be preserved, got %+v", items[1])
|
|
}
|
|
if items[0].SourceKindLabel != "一级官方发布" || items[0].PrimarySource != "https://open.bigmodel.cn/dev/howuse/model" {
|
|
t.Fatalf("expected official release 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: "价格下降影响默认成本。",
|
|
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: "同日另有上调记录。",
|
|
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: "新模型上线。",
|
|
SourceKindLabel: "模型快照",
|
|
PrimarySource: "OpenRouter / region_pricing",
|
|
UpdatedAt: "2026-05-13 09:00",
|
|
EvidenceDetail: "models.created_at = 今日,且已存在最新价格快照",
|
|
Priority: 70,
|
|
},
|
|
}
|
|
|
|
items := buildHeadlineItems(report)
|
|
|
|
if len(items) != 2 {
|
|
t.Fatalf("expected 2 deduplicated headline items, got %d", len(items))
|
|
}
|
|
if strings.Contains(items[1].Title, "GPT-4o") {
|
|
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: "新模型进入情报池。",
|
|
Audience: "适合想尽快验证新模型价值的读者",
|
|
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)
|
|
}
|
|
if item.Audience != "适合想尽快验证新模型价值的读者" {
|
|
t.Fatalf("expected audience to be propagated, got %+v", item)
|
|
}
|
|
}
|
|
|
|
func TestHeadlineItemFromOfficialReleaseEvent(t *testing.T) {
|
|
item := headlineItemFromModelEvent(ModelEvent{
|
|
EventType: "official_release",
|
|
ModelName: "Claude Sonnet 4.5",
|
|
TrustLabel: "官方来源",
|
|
Baseline: "官方首次发布",
|
|
Summary: "官方发布新模型。",
|
|
SourceKindLabel: "一级官方发布",
|
|
PrimarySource: "https://docs.anthropic.com/en/release-notes/api",
|
|
UpdatedAt: "2026-05-13 07:00",
|
|
EvidenceDetail: "models.release_date = 今日,且 source_url 指向官方发布页",
|
|
})
|
|
|
|
if item.Label != "一级官方发布" {
|
|
t.Fatalf("expected label to be 一级官方发布, got %+v", item)
|
|
}
|
|
if !strings.Contains(item.Title, "官方发布") {
|
|
t.Fatalf("expected title to mention 官方发布, got %+v", item)
|
|
}
|
|
if item.SourceKindLabel != "一级官方发布" {
|
|
t.Fatalf("expected source kind label to be 一级官方发布, got %+v", item)
|
|
}
|
|
if item.PrimarySource != "https://docs.anthropic.com/en/release-notes/api" {
|
|
t.Fatalf("expected primary source to be preserved, got %+v", item)
|
|
}
|
|
if item.Tone != "official-primary" {
|
|
t.Fatalf("expected tone to be official-primary, got %+v", item)
|
|
}
|
|
}
|
|
|
|
func TestHeadlineItemFromSecondaryReleaseEvent(t *testing.T) {
|
|
item := headlineItemFromModelEvent(ModelEvent{
|
|
EventType: "official_release",
|
|
ModelName: "Doubao Seed 1.8",
|
|
TrustLabel: "官方来源 / 二级佐证",
|
|
Baseline: "官方首次发布",
|
|
Summary: "模型进入正式发布日期观察池。",
|
|
SourceKindLabel: "二级权威佐证发布",
|
|
PrimarySource: "https://developer.volcengine.com/articles/7601918680544641034",
|
|
UpdatedAt: "2025-12-18 00:00",
|
|
EvidenceDetail: "models.release_date = 今日,发布日期采用次级权威报道佐证,模型来源页保留官方文档",
|
|
})
|
|
|
|
if item.Label != "二级权威佐证" {
|
|
t.Fatalf("expected label to be 二级权威佐证, got %+v", item)
|
|
}
|
|
if !strings.Contains(item.Title, "权威佐证发布时间线") {
|
|
t.Fatalf("expected title to mention 权威佐证发布时间线, got %+v", item)
|
|
}
|
|
if item.Tone != "secondary-evidence" {
|
|
t.Fatalf("expected tone to be secondary-evidence, got %+v", item)
|
|
}
|
|
}
|
|
|
|
func TestHeadlineItemFromPromoCampaignEvent(t *testing.T) {
|
|
item := headlineItemFromModelEvent(ModelEvent{
|
|
EventType: "promo_campaign",
|
|
ModelName: "DeepSeek-V3.2-Exp",
|
|
TrustLabel: "官方来源 / 一级证据",
|
|
Baseline: "活动窗口开启",
|
|
Summary: "官方活动窗口出现后,值得重新评估低成本推理方案。",
|
|
Audience: "适合计划趁活动窗口压低推理成本的团队",
|
|
SourceKindLabel: "官方活动页",
|
|
PrimarySource: "https://api-docs.deepseek.com/news/news250929",
|
|
UpdatedAt: "2025-09-29 00:00",
|
|
EvidenceDetail: "官方活动页记录 V3.2-Exp 在活动窗口内价格下调 50%+",
|
|
})
|
|
|
|
if item.Label != "营销活动" {
|
|
t.Fatalf("expected label to be 营销活动, got %+v", item)
|
|
}
|
|
if !strings.Contains(item.Title, "活动窗口") {
|
|
t.Fatalf("expected title to mention 活动窗口, got %+v", item)
|
|
}
|
|
if item.Tone != "promo" {
|
|
t.Fatalf("expected tone to be promo, got %+v", item)
|
|
}
|
|
if item.Audience != "适合计划趁活动窗口压低推理成本的团队" {
|
|
t.Fatalf("expected audience to be preserved, got %+v", item)
|
|
}
|
|
}
|