feat(import): track release date evidence tiers
This commit is contained in:
34
db/migrations/006_model_release_date_metadata.sql
Normal file
34
db/migrations/006_model_release_date_metadata.sql
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
-- Phase 2.1: 模型发布日期证据元数据
|
||||||
|
-- 区分一级官方发布日期与二级权威佐证日期,避免混淆 source_url 与发布日期证据层级
|
||||||
|
|
||||||
|
DO $$
|
||||||
|
BEGIN
|
||||||
|
IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name='models' AND column_name='date_confidence') THEN
|
||||||
|
ALTER TABLE models ADD COLUMN date_confidence TEXT NOT NULL DEFAULT 'unknown';
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name='models' AND column_name='date_source_kind') THEN
|
||||||
|
ALTER TABLE models ADD COLUMN date_source_kind TEXT NOT NULL DEFAULT 'unknown';
|
||||||
|
END IF;
|
||||||
|
END $$;
|
||||||
|
|
||||||
|
DO $$
|
||||||
|
BEGIN
|
||||||
|
IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname='chk_models_date_confidence') THEN
|
||||||
|
ALTER TABLE models
|
||||||
|
ADD CONSTRAINT chk_models_date_confidence
|
||||||
|
CHECK (date_confidence IN ('official_primary', 'secondary_authoritative', 'inferred', 'unknown'));
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname='chk_models_date_source_kind') THEN
|
||||||
|
ALTER TABLE models
|
||||||
|
ADD CONSTRAINT chk_models_date_source_kind
|
||||||
|
CHECK (date_source_kind IN ('official_announcement', 'official_product_page', 'secondary_authoritative_report', 'catalog_backfill', 'unknown'));
|
||||||
|
END IF;
|
||||||
|
END $$;
|
||||||
|
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_models_date_confidence ON models(date_confidence);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_models_date_source_kind ON models(date_source_kind);
|
||||||
|
|
||||||
|
COMMENT ON COLUMN models.date_confidence IS '发布日期证据置信度:official_primary / secondary_authoritative / inferred / unknown';
|
||||||
|
COMMENT ON COLUMN models.date_source_kind IS '发布日期证据来源类型:official_announcement / official_product_page / secondary_authoritative_report / catalog_backfill / unknown';
|
||||||
@@ -239,14 +239,14 @@ type ModelEvent struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type Recommendation struct {
|
type Recommendation struct {
|
||||||
Name string
|
Name string
|
||||||
Provider string
|
Provider string
|
||||||
Operator string
|
Operator string
|
||||||
Usage string
|
Usage string
|
||||||
PriceSummary string
|
PriceSummary string
|
||||||
Evidence string
|
Evidence string
|
||||||
TrustLabel string
|
TrustLabel string
|
||||||
Tags []string
|
Tags []string
|
||||||
}
|
}
|
||||||
|
|
||||||
type SceneSection struct {
|
type SceneSection struct {
|
||||||
@@ -812,6 +812,8 @@ func loadOfficialReleaseEvents(db *sql.DB, date string) ([]ModelEvent, error) {
|
|||||||
COALESCE(lp.operator_name, 'Unknown') AS operator_name,
|
COALESCE(lp.operator_name, 'Unknown') AS operator_name,
|
||||||
COALESCE(lp.operator_type, 'reseller') AS operator_type,
|
COALESCE(lp.operator_type, 'reseller') AS operator_type,
|
||||||
COALESCE(m.source_url, '') AS source_url,
|
COALESCE(m.source_url, '') AS source_url,
|
||||||
|
COALESCE(m.date_confidence, 'unknown') AS date_confidence,
|
||||||
|
COALESCE(m.date_source_kind, 'unknown') AS date_source_kind,
|
||||||
COALESCE(mp.country, 'unknown') AS provider_country,
|
COALESCE(mp.country, 'unknown') AS provider_country,
|
||||||
COALESCE(m.release_date, m.created_at::date) AS release_date,
|
COALESCE(m.release_date, m.created_at::date) AS release_date,
|
||||||
COALESCE(lp.currency, 'USD') AS currency
|
COALESCE(lp.currency, 'USD') AS currency
|
||||||
@@ -838,6 +840,8 @@ func loadOfficialReleaseEvents(db *sql.DB, date string) ([]ModelEvent, error) {
|
|||||||
operatorName string
|
operatorName string
|
||||||
operatorType string
|
operatorType string
|
||||||
sourceURL string
|
sourceURL string
|
||||||
|
dateConfidence string
|
||||||
|
dateSourceKind string
|
||||||
providerCountry string
|
providerCountry string
|
||||||
releaseDate time.Time
|
releaseDate time.Time
|
||||||
currency string
|
currency string
|
||||||
@@ -848,6 +852,8 @@ func loadOfficialReleaseEvents(db *sql.DB, date string) ([]ModelEvent, error) {
|
|||||||
&operatorName,
|
&operatorName,
|
||||||
&operatorType,
|
&operatorType,
|
||||||
&sourceURL,
|
&sourceURL,
|
||||||
|
&dateConfidence,
|
||||||
|
&dateSourceKind,
|
||||||
&providerCountry,
|
&providerCountry,
|
||||||
&releaseDate,
|
&releaseDate,
|
||||||
¤cy,
|
¤cy,
|
||||||
@@ -869,11 +875,11 @@ func loadOfficialReleaseEvents(db *sql.DB, date string) ([]ModelEvent, error) {
|
|||||||
ModelName: modelName,
|
ModelName: modelName,
|
||||||
ProviderName: providerName,
|
ProviderName: providerName,
|
||||||
OperatorName: operatorName,
|
OperatorName: operatorName,
|
||||||
TrustLabel: buildTrustLabel(model),
|
TrustLabel: buildReleaseTrustLabel(model, dateConfidence),
|
||||||
SourceKindLabel: "官方发布",
|
SourceKindLabel: buildReleaseSourceKindLabel(dateSourceKind, dateConfidence),
|
||||||
PrimarySource: sourceURL,
|
PrimarySource: sourceURL,
|
||||||
UpdatedAt: releaseDate.Format("2006-01-02 15:04"),
|
UpdatedAt: releaseDate.Format("2006-01-02 15:04"),
|
||||||
EvidenceDetail: "models.release_date = 今日,且 source_url 指向官方发布页",
|
EvidenceDetail: buildReleaseEvidenceDetail(dateSourceKind, dateConfidence),
|
||||||
Baseline: "官方首次发布",
|
Baseline: "官方首次发布",
|
||||||
Summary: fmt.Sprintf("%s 官方发布新模型,值得优先复查默认选型。", providerName),
|
Summary: fmt.Sprintf("%s 官方发布新模型,值得优先复查默认选型。", providerName),
|
||||||
Currency: currency,
|
Currency: currency,
|
||||||
@@ -991,7 +997,7 @@ func loadNewModelEvents(db *sql.DB, date string) ([]ModelEvent, error) {
|
|||||||
Summary: summary,
|
Summary: summary,
|
||||||
Currency: currency,
|
Currency: currency,
|
||||||
NewInputPrice: inputPrice,
|
NewInputPrice: inputPrice,
|
||||||
NewOutputPrice: outputPrice,
|
NewOutputPrice: outputPrice,
|
||||||
Priority: 85 + minInt(contextLength/(1024*128), 10),
|
Priority: 85 + minInt(contextLength/(1024*128), 10),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -1247,7 +1253,7 @@ func buildFreeSourceBreakdown(models []ModelInfo) []FreeSourceStat {
|
|||||||
counts := map[string]int{
|
counts := map[string]int{
|
||||||
"官方免费": 0,
|
"官方免费": 0,
|
||||||
"聚合免费": 0,
|
"聚合免费": 0,
|
||||||
"待确认": 0,
|
"待确认": 0,
|
||||||
}
|
}
|
||||||
for _, model := range models {
|
for _, model := range models {
|
||||||
counts[classifyFreeSource(model)]++
|
counts[classifyFreeSource(model)]++
|
||||||
@@ -1478,6 +1484,10 @@ func headlineItemFromModelEvent(event ModelEvent) HeadlineItem {
|
|||||||
case "official_release":
|
case "official_release":
|
||||||
item.Label = "官方发布"
|
item.Label = "官方发布"
|
||||||
item.Title = fmt.Sprintf("%s 官方发布", event.ModelName)
|
item.Title = fmt.Sprintf("%s 官方发布", event.ModelName)
|
||||||
|
if event.SourceKindLabel == "权威佐证发布" {
|
||||||
|
item.Label = "权威佐证"
|
||||||
|
item.Title = fmt.Sprintf("%s 进入权威佐证发布时间线", event.ModelName)
|
||||||
|
}
|
||||||
item.Tone = "info"
|
item.Tone = "info"
|
||||||
case "new_model":
|
case "new_model":
|
||||||
item.Label = "新模型"
|
item.Label = "新模型"
|
||||||
@@ -1534,6 +1544,48 @@ func buildPriceEvidenceDetail(changePct, oldPrice, newPrice float64, currency st
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func buildReleaseSourceKindLabel(dateSourceKind, dateConfidence string) string {
|
||||||
|
switch {
|
||||||
|
case dateSourceKind == "secondary_authoritative_report" || dateConfidence == "secondary_authoritative":
|
||||||
|
return "权威佐证发布"
|
||||||
|
case dateSourceKind == "official_announcement" && dateConfidence == "official_primary":
|
||||||
|
return "官方发布"
|
||||||
|
case dateSourceKind == "official_product_page":
|
||||||
|
return "官方产品页"
|
||||||
|
case dateSourceKind == "catalog_backfill":
|
||||||
|
return "目录回填"
|
||||||
|
default:
|
||||||
|
return "官方发布"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func buildReleaseEvidenceDetail(dateSourceKind, dateConfidence string) string {
|
||||||
|
switch {
|
||||||
|
case dateSourceKind == "secondary_authoritative_report" || dateConfidence == "secondary_authoritative":
|
||||||
|
return "models.release_date = 今日,发布日期采用次级权威报道佐证,模型来源页保留官方文档"
|
||||||
|
case dateSourceKind == "official_announcement" && dateConfidence == "official_primary":
|
||||||
|
return "models.release_date = 今日,且 source_url 指向官方发布页"
|
||||||
|
case dateSourceKind == "official_product_page":
|
||||||
|
return "models.release_date = 今日,来源页为官方产品页,发布日期置信度待确认"
|
||||||
|
case dateSourceKind == "catalog_backfill":
|
||||||
|
return "models.release_date = 今日,发布日期来自目录级元数据回填"
|
||||||
|
default:
|
||||||
|
return "models.release_date = 今日,且已记录发布日期证据元数据"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func buildReleaseTrustLabel(model ModelInfo, dateConfidence string) string {
|
||||||
|
base := buildTrustLabel(model)
|
||||||
|
switch dateConfidence {
|
||||||
|
case "official_primary":
|
||||||
|
return base + " / 一级证据"
|
||||||
|
case "secondary_authoritative":
|
||||||
|
return base + " / 二级佐证"
|
||||||
|
default:
|
||||||
|
return base
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func buildFreeEvidenceDetail(model ModelInfo) string {
|
func buildFreeEvidenceDetail(model ModelInfo) string {
|
||||||
switch classifyFreeSource(model) {
|
switch classifyFreeSource(model) {
|
||||||
case "官方免费":
|
case "官方免费":
|
||||||
|
|||||||
@@ -16,146 +16,146 @@ func sampleReportForV1() *ReportV3 {
|
|||||||
TotalModels: 504,
|
TotalModels: 504,
|
||||||
AllModels: []ModelInfo{
|
AllModels: []ModelInfo{
|
||||||
{
|
{
|
||||||
Name: "DeepSeek-V4-Flash",
|
Name: "DeepSeek-V4-Flash",
|
||||||
ProviderName: "DeepSeek",
|
ProviderName: "DeepSeek",
|
||||||
ProviderCountry:"CN",
|
ProviderCountry: "CN",
|
||||||
ContextLength: 262144,
|
ContextLength: 262144,
|
||||||
InputPrice: 0.30,
|
InputPrice: 0.30,
|
||||||
OutputPrice: 1.20,
|
OutputPrice: 1.20,
|
||||||
Currency: "USD",
|
Currency: "USD",
|
||||||
OperatorName: "OpenRouter",
|
OperatorName: "OpenRouter",
|
||||||
OperatorType: "reseller",
|
OperatorType: "reseller",
|
||||||
Region: "global",
|
Region: "global",
|
||||||
SceneTags: []SceneTag{SceneCode, SceneReasoning},
|
SceneTags: []SceneTag{SceneCode, SceneReasoning},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "glm-5",
|
Name: "glm-5",
|
||||||
ProviderName: "Zhipu",
|
ProviderName: "Zhipu",
|
||||||
ProviderCountry:"CN",
|
ProviderCountry: "CN",
|
||||||
ContextLength: 131072,
|
ContextLength: 131072,
|
||||||
InputPrice: 0,
|
InputPrice: 0,
|
||||||
OutputPrice: 0,
|
OutputPrice: 0,
|
||||||
Currency: "CNY",
|
Currency: "CNY",
|
||||||
IsFree: true,
|
IsFree: true,
|
||||||
OperatorName: "Zhipu",
|
OperatorName: "Zhipu",
|
||||||
OperatorType: "official",
|
OperatorType: "official",
|
||||||
Region: "cn",
|
Region: "cn",
|
||||||
SceneTags: []SceneTag{SceneWriting, SceneChat},
|
SceneTags: []SceneTag{SceneWriting, SceneChat},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "claude-3.7-sonnet",
|
Name: "claude-3.7-sonnet",
|
||||||
ProviderName: "Anthropic",
|
ProviderName: "Anthropic",
|
||||||
ProviderCountry:"US",
|
ProviderCountry: "US",
|
||||||
ContextLength: 200000,
|
ContextLength: 200000,
|
||||||
InputPrice: 3.0,
|
InputPrice: 3.0,
|
||||||
OutputPrice: 15.0,
|
OutputPrice: 15.0,
|
||||||
Currency: "USD",
|
Currency: "USD",
|
||||||
OperatorName: "Anthropic",
|
OperatorName: "Anthropic",
|
||||||
OperatorType: "official",
|
OperatorType: "official",
|
||||||
Region: "global",
|
Region: "global",
|
||||||
SceneTags: []SceneTag{SceneWriting, SceneChat},
|
SceneTags: []SceneTag{SceneWriting, SceneChat},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "qwen-vl-max",
|
Name: "qwen-vl-max",
|
||||||
ProviderName: "Alibaba",
|
ProviderName: "Alibaba",
|
||||||
ProviderCountry:"CN",
|
ProviderCountry: "CN",
|
||||||
ContextLength: 65536,
|
ContextLength: 65536,
|
||||||
InputPrice: 0.8,
|
InputPrice: 0.8,
|
||||||
OutputPrice: 2.4,
|
OutputPrice: 2.4,
|
||||||
Currency: "CNY",
|
Currency: "CNY",
|
||||||
OperatorName: "DashScope",
|
OperatorName: "DashScope",
|
||||||
OperatorType: "cloud",
|
OperatorType: "cloud",
|
||||||
Region: "cn",
|
Region: "cn",
|
||||||
SceneTags: []SceneTag{SceneVision},
|
SceneTags: []SceneTag{SceneVision},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
FreeModels: []ModelInfo{
|
FreeModels: []ModelInfo{
|
||||||
{
|
{
|
||||||
Name: "glm-5",
|
Name: "glm-5",
|
||||||
ProviderName: "Zhipu",
|
ProviderName: "Zhipu",
|
||||||
ProviderCountry:"CN",
|
ProviderCountry: "CN",
|
||||||
ContextLength: 131072,
|
ContextLength: 131072,
|
||||||
Currency: "CNY",
|
Currency: "CNY",
|
||||||
IsFree: true,
|
IsFree: true,
|
||||||
OperatorName: "Zhipu",
|
OperatorName: "Zhipu",
|
||||||
OperatorType: "official",
|
OperatorType: "official",
|
||||||
Region: "cn",
|
Region: "cn",
|
||||||
SceneTags: []SceneTag{SceneWriting, SceneChat},
|
SceneTags: []SceneTag{SceneWriting, SceneChat},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "DeepSeek-V4-Flash",
|
Name: "DeepSeek-V4-Flash",
|
||||||
ProviderName: "DeepSeek",
|
ProviderName: "DeepSeek",
|
||||||
ProviderCountry:"CN",
|
ProviderCountry: "CN",
|
||||||
ContextLength: 262144,
|
ContextLength: 262144,
|
||||||
Currency: "USD",
|
Currency: "USD",
|
||||||
IsFree: true,
|
IsFree: true,
|
||||||
OperatorName: "OpenRouter",
|
OperatorName: "OpenRouter",
|
||||||
OperatorType: "reseller",
|
OperatorType: "reseller",
|
||||||
Region: "global",
|
Region: "global",
|
||||||
SceneTags: []SceneTag{SceneCode, SceneReasoning},
|
SceneTags: []SceneTag{SceneCode, SceneReasoning},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "mystery-free-model",
|
Name: "mystery-free-model",
|
||||||
ProviderName: "Unknown",
|
ProviderName: "Unknown",
|
||||||
ProviderCountry:"unknown",
|
ProviderCountry: "unknown",
|
||||||
ContextLength: 65536,
|
ContextLength: 65536,
|
||||||
Currency: "USD",
|
Currency: "USD",
|
||||||
IsFree: true,
|
IsFree: true,
|
||||||
OperatorName: "Unknown Gateway",
|
OperatorName: "Unknown Gateway",
|
||||||
OperatorType: "self_hosted_gateway",
|
OperatorType: "self_hosted_gateway",
|
||||||
Region: "global",
|
Region: "global",
|
||||||
SceneTags: []SceneTag{SceneChat},
|
SceneTags: []SceneTag{SceneChat},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
FreeTop20: []ModelInfo{
|
FreeTop20: []ModelInfo{
|
||||||
{
|
{
|
||||||
Name: "glm-5",
|
Name: "glm-5",
|
||||||
ProviderName: "Zhipu",
|
ProviderName: "Zhipu",
|
||||||
ProviderCountry:"CN",
|
ProviderCountry: "CN",
|
||||||
ContextLength: 131072,
|
ContextLength: 131072,
|
||||||
Currency: "CNY",
|
Currency: "CNY",
|
||||||
IsFree: true,
|
IsFree: true,
|
||||||
OperatorName: "Zhipu",
|
OperatorName: "Zhipu",
|
||||||
OperatorType: "official",
|
OperatorType: "official",
|
||||||
Region: "cn",
|
Region: "cn",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
IntlTop5: []ModelInfo{
|
IntlTop5: []ModelInfo{
|
||||||
{
|
{
|
||||||
Name: "DeepSeek-V4-Flash",
|
Name: "DeepSeek-V4-Flash",
|
||||||
ProviderName: "DeepSeek",
|
ProviderName: "DeepSeek",
|
||||||
ProviderCountry:"CN",
|
ProviderCountry: "CN",
|
||||||
ContextLength: 262144,
|
ContextLength: 262144,
|
||||||
InputPrice: 0.30,
|
InputPrice: 0.30,
|
||||||
OutputPrice: 1.20,
|
OutputPrice: 1.20,
|
||||||
Currency: "USD",
|
Currency: "USD",
|
||||||
OperatorName: "OpenRouter",
|
OperatorName: "OpenRouter",
|
||||||
OperatorType: "reseller",
|
OperatorType: "reseller",
|
||||||
Region: "global",
|
Region: "global",
|
||||||
SceneTags: []SceneTag{SceneCode, SceneReasoning},
|
SceneTags: []SceneTag{SceneCode, SceneReasoning},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
DomesticTop10: []ModelInfo{
|
DomesticTop10: []ModelInfo{
|
||||||
{
|
{
|
||||||
Name: "qwen-vl-max",
|
Name: "qwen-vl-max",
|
||||||
ProviderName: "Alibaba",
|
ProviderName: "Alibaba",
|
||||||
ProviderCountry:"CN",
|
ProviderCountry: "CN",
|
||||||
ContextLength: 65536,
|
ContextLength: 65536,
|
||||||
InputPrice: 0.8,
|
InputPrice: 0.8,
|
||||||
OutputPrice: 2.4,
|
OutputPrice: 2.4,
|
||||||
Currency: "CNY",
|
Currency: "CNY",
|
||||||
OperatorName: "DashScope",
|
OperatorName: "DashScope",
|
||||||
OperatorType: "cloud",
|
OperatorType: "cloud",
|
||||||
Region: "cn",
|
Region: "cn",
|
||||||
SceneTags: []SceneTag{SceneVision},
|
SceneTags: []SceneTag{SceneVision},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
DailySignals: DailySignals{
|
DailySignals: DailySignals{
|
||||||
NewModels: 2,
|
NewModels: 2,
|
||||||
PriceChanges: 1,
|
PriceChanges: 1,
|
||||||
OfficialFree: 1,
|
OfficialFree: 1,
|
||||||
AggregatorFree:1,
|
AggregatorFree: 1,
|
||||||
UnknownFree: 1,
|
UnknownFree: 1,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -184,33 +184,33 @@ func TestDecorateReportV1BuildsHotDaySummary(t *testing.T) {
|
|||||||
report := sampleReportForV1()
|
report := sampleReportForV1()
|
||||||
report.ModelEvents = []ModelEvent{
|
report.ModelEvents = []ModelEvent{
|
||||||
{
|
{
|
||||||
EventType: "new_model",
|
EventType: "new_model",
|
||||||
ModelName: "DeepSeek-V4-Flash",
|
ModelName: "DeepSeek-V4-Flash",
|
||||||
ProviderName: "DeepSeek",
|
ProviderName: "DeepSeek",
|
||||||
OperatorName: "OpenRouter",
|
OperatorName: "OpenRouter",
|
||||||
TrustLabel: "聚合来源",
|
TrustLabel: "聚合来源",
|
||||||
Baseline: "首次出现",
|
Baseline: "首次出现",
|
||||||
Summary: "新模型进入情报池,值得重新评估低成本编码默认选择。",
|
Summary: "新模型进入情报池,值得重新评估低成本编码默认选择。",
|
||||||
SourceKindLabel: "模型快照",
|
SourceKindLabel: "模型快照",
|
||||||
PrimarySource: "OpenRouter / region_pricing",
|
PrimarySource: "OpenRouter / region_pricing",
|
||||||
UpdatedAt: "2026-05-13 09:30",
|
UpdatedAt: "2026-05-13 09:30",
|
||||||
EvidenceDetail: "models.created_at = 今日,且已存在最新价格快照",
|
EvidenceDetail: "models.created_at = 今日,且已存在最新价格快照",
|
||||||
Priority: 95,
|
Priority: 95,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
EventType: "price_cut",
|
EventType: "price_cut",
|
||||||
ModelName: "qwen-vl-max",
|
ModelName: "qwen-vl-max",
|
||||||
ProviderName: "Alibaba",
|
ProviderName: "Alibaba",
|
||||||
OperatorName: "DashScope",
|
OperatorName: "DashScope",
|
||||||
TrustLabel: "官方来源",
|
TrustLabel: "官方来源",
|
||||||
Baseline: "较昨日 -18%",
|
Baseline: "较昨日 -18%",
|
||||||
Summary: "价格下降已足以影响视觉模型默认选择。",
|
Summary: "价格下降已足以影响视觉模型默认选择。",
|
||||||
SourceKindLabel: "价格快照",
|
SourceKindLabel: "价格快照",
|
||||||
PrimarySource: "pricing_history",
|
PrimarySource: "pricing_history",
|
||||||
UpdatedAt: "2026-05-13 10:00",
|
UpdatedAt: "2026-05-13 10:00",
|
||||||
EvidenceDetail: "pricing_history 记录到输入价格较昨日下降 18%",
|
EvidenceDetail: "pricing_history 记录到输入价格较昨日下降 18%",
|
||||||
PriceChangePct: -18,
|
PriceChangePct: -18,
|
||||||
Priority: 90,
|
Priority: 90,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -287,18 +287,18 @@ func TestGenerateMarkdownV3IncludesTencentSubscriptionSection(t *testing.T) {
|
|||||||
}
|
}
|
||||||
report.ModelEvents = []ModelEvent{
|
report.ModelEvents = []ModelEvent{
|
||||||
{
|
{
|
||||||
EventType: "new_model",
|
EventType: "new_model",
|
||||||
ModelName: "DeepSeek-V4-Flash",
|
ModelName: "DeepSeek-V4-Flash",
|
||||||
ProviderName: "DeepSeek",
|
ProviderName: "DeepSeek",
|
||||||
OperatorName: "OpenRouter",
|
OperatorName: "OpenRouter",
|
||||||
TrustLabel: "聚合来源",
|
TrustLabel: "聚合来源",
|
||||||
Baseline: "首次出现",
|
Baseline: "首次出现",
|
||||||
Summary: "新模型进入情报池,值得重新评估低成本编码默认选择。",
|
Summary: "新模型进入情报池,值得重新评估低成本编码默认选择。",
|
||||||
SourceKindLabel: "模型快照",
|
SourceKindLabel: "模型快照",
|
||||||
PrimarySource: "OpenRouter / region_pricing",
|
PrimarySource: "OpenRouter / region_pricing",
|
||||||
UpdatedAt: "2026-05-13 09:30",
|
UpdatedAt: "2026-05-13 09:30",
|
||||||
EvidenceDetail: "models.created_at = 今日,且已存在最新价格快照",
|
EvidenceDetail: "models.created_at = 今日,且已存在最新价格快照",
|
||||||
Priority: 95,
|
Priority: 95,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
decorateReportV1(report)
|
decorateReportV1(report)
|
||||||
@@ -340,18 +340,18 @@ func TestGenerateHTMLV3IncludesTencentSubscriptionSection(t *testing.T) {
|
|||||||
report := sampleReportForV1()
|
report := sampleReportForV1()
|
||||||
report.ModelEvents = []ModelEvent{
|
report.ModelEvents = []ModelEvent{
|
||||||
{
|
{
|
||||||
EventType: "new_model",
|
EventType: "new_model",
|
||||||
ModelName: "DeepSeek-V4-Flash",
|
ModelName: "DeepSeek-V4-Flash",
|
||||||
ProviderName: "DeepSeek",
|
ProviderName: "DeepSeek",
|
||||||
OperatorName: "OpenRouter",
|
OperatorName: "OpenRouter",
|
||||||
TrustLabel: "聚合来源",
|
TrustLabel: "聚合来源",
|
||||||
Baseline: "首次出现",
|
Baseline: "首次出现",
|
||||||
Summary: "新模型进入情报池,值得重新评估低成本编码默认选择。",
|
Summary: "新模型进入情报池,值得重新评估低成本编码默认选择。",
|
||||||
SourceKindLabel: "模型快照",
|
SourceKindLabel: "模型快照",
|
||||||
PrimarySource: "OpenRouter / region_pricing",
|
PrimarySource: "OpenRouter / region_pricing",
|
||||||
UpdatedAt: "2026-05-13 09:30",
|
UpdatedAt: "2026-05-13 09:30",
|
||||||
EvidenceDetail: "models.created_at = 今日,且已存在最新价格快照",
|
EvidenceDetail: "models.created_at = 今日,且已存在最新价格快照",
|
||||||
Priority: 95,
|
Priority: 95,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
report.TencentSubscriptionPlans = []SubscriptionPlanInfo{
|
report.TencentSubscriptionPlans = []SubscriptionPlanInfo{
|
||||||
@@ -406,47 +406,47 @@ func TestBuildHeadlineItemsUsesModelEvents(t *testing.T) {
|
|||||||
report := sampleReportForV1()
|
report := sampleReportForV1()
|
||||||
report.ModelEvents = []ModelEvent{
|
report.ModelEvents = []ModelEvent{
|
||||||
{
|
{
|
||||||
EventType: "official_release",
|
EventType: "official_release",
|
||||||
ModelName: "GLM-5",
|
ModelName: "GLM-5",
|
||||||
ProviderName: "Zhipu",
|
ProviderName: "Zhipu",
|
||||||
OperatorName: "Zhipu",
|
OperatorName: "Zhipu",
|
||||||
TrustLabel: "官方来源",
|
TrustLabel: "官方来源",
|
||||||
Baseline: "官方首次发布",
|
Baseline: "官方首次发布",
|
||||||
Summary: "官方发布新模型,值得优先复查中文通用与推理场景默认选择。",
|
Summary: "官方发布新模型,值得优先复查中文通用与推理场景默认选择。",
|
||||||
SourceKindLabel: "官方发布",
|
SourceKindLabel: "官方发布",
|
||||||
PrimarySource: "https://open.bigmodel.cn/dev/howuse/model",
|
PrimarySource: "https://open.bigmodel.cn/dev/howuse/model",
|
||||||
UpdatedAt: "2026-05-13 08:30",
|
UpdatedAt: "2026-05-13 08:30",
|
||||||
EvidenceDetail: "models.release_date = 今日,且 source_url 指向官方文档",
|
EvidenceDetail: "models.release_date = 今日,且 source_url 指向官方文档",
|
||||||
Priority: 120,
|
Priority: 120,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
EventType: "price_cut",
|
EventType: "price_cut",
|
||||||
ModelName: "glm-5",
|
ModelName: "glm-5",
|
||||||
ProviderName: "Zhipu",
|
ProviderName: "Zhipu",
|
||||||
OperatorName: "Zhipu",
|
OperatorName: "Zhipu",
|
||||||
TrustLabel: "官方来源",
|
TrustLabel: "官方来源",
|
||||||
Baseline: "较昨日 -25%",
|
Baseline: "较昨日 -25%",
|
||||||
Summary: "价格下降已足以影响中文通用场景默认选型。",
|
Summary: "价格下降已足以影响中文通用场景默认选型。",
|
||||||
SourceKindLabel: "价格快照",
|
SourceKindLabel: "价格快照",
|
||||||
PrimarySource: "pricing_history",
|
PrimarySource: "pricing_history",
|
||||||
UpdatedAt: "2026-05-13 10:00",
|
UpdatedAt: "2026-05-13 10:00",
|
||||||
EvidenceDetail: "pricing_history 记录到输入价格较昨日下降 25%",
|
EvidenceDetail: "pricing_history 记录到输入价格较昨日下降 25%",
|
||||||
PriceChangePct: -25,
|
PriceChangePct: -25,
|
||||||
Priority: 100,
|
Priority: 100,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
EventType: "new_model",
|
EventType: "new_model",
|
||||||
ModelName: "DeepSeek-V4-Flash",
|
ModelName: "DeepSeek-V4-Flash",
|
||||||
ProviderName: "DeepSeek",
|
ProviderName: "DeepSeek",
|
||||||
OperatorName: "OpenRouter",
|
OperatorName: "OpenRouter",
|
||||||
TrustLabel: "聚合来源",
|
TrustLabel: "聚合来源",
|
||||||
Baseline: "首次出现",
|
Baseline: "首次出现",
|
||||||
Summary: "新模型进入情报池,值得重新评估低成本编码默认选择。",
|
Summary: "新模型进入情报池,值得重新评估低成本编码默认选择。",
|
||||||
SourceKindLabel: "模型快照",
|
SourceKindLabel: "模型快照",
|
||||||
PrimarySource: "OpenRouter / region_pricing",
|
PrimarySource: "OpenRouter / region_pricing",
|
||||||
UpdatedAt: "2026-05-13 09:30",
|
UpdatedAt: "2026-05-13 09:30",
|
||||||
EvidenceDetail: "models.created_at = 今日,且已存在最新价格快照",
|
EvidenceDetail: "models.created_at = 今日,且已存在最新价格快照",
|
||||||
Priority: 90,
|
Priority: 90,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -470,45 +470,45 @@ func TestBuildHeadlineItemsDeduplicatesSameModel(t *testing.T) {
|
|||||||
report := sampleReportForV1()
|
report := sampleReportForV1()
|
||||||
report.ModelEvents = []ModelEvent{
|
report.ModelEvents = []ModelEvent{
|
||||||
{
|
{
|
||||||
EventType: "price_cut",
|
EventType: "price_cut",
|
||||||
ModelName: "OpenAI: GPT-4o",
|
ModelName: "OpenAI: GPT-4o",
|
||||||
ProviderName: "OpenAI",
|
ProviderName: "OpenAI",
|
||||||
TrustLabel: "官方来源",
|
TrustLabel: "官方来源",
|
||||||
Baseline: "较昨日 -20%",
|
Baseline: "较昨日 -20%",
|
||||||
Summary: "价格下降影响默认成本。",
|
Summary: "价格下降影响默认成本。",
|
||||||
SourceKindLabel: "价格快照",
|
SourceKindLabel: "价格快照",
|
||||||
PrimarySource: "pricing_history",
|
PrimarySource: "pricing_history",
|
||||||
UpdatedAt: "2026-05-13 10:00",
|
UpdatedAt: "2026-05-13 10:00",
|
||||||
EvidenceDetail: "pricing_history 记录到输入价格较昨日下降 20%",
|
EvidenceDetail: "pricing_history 记录到输入价格较昨日下降 20%",
|
||||||
PriceChangePct: -20,
|
PriceChangePct: -20,
|
||||||
Priority: 95,
|
Priority: 95,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
EventType: "price_increase",
|
EventType: "price_increase",
|
||||||
ModelName: "OpenAI: GPT-4o",
|
ModelName: "OpenAI: GPT-4o",
|
||||||
ProviderName: "OpenAI",
|
ProviderName: "OpenAI",
|
||||||
TrustLabel: "官方来源",
|
TrustLabel: "官方来源",
|
||||||
Baseline: "较昨日 +5%",
|
Baseline: "较昨日 +5%",
|
||||||
Summary: "同日另有上调记录。",
|
Summary: "同日另有上调记录。",
|
||||||
SourceKindLabel: "价格快照",
|
SourceKindLabel: "价格快照",
|
||||||
PrimarySource: "pricing_history",
|
PrimarySource: "pricing_history",
|
||||||
UpdatedAt: "2026-05-13 11:00",
|
UpdatedAt: "2026-05-13 11:00",
|
||||||
EvidenceDetail: "pricing_history 记录到输入价格较昨日上涨 5%",
|
EvidenceDetail: "pricing_history 记录到输入价格较昨日上涨 5%",
|
||||||
PriceChangePct: 5,
|
PriceChangePct: 5,
|
||||||
Priority: 80,
|
Priority: 80,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
EventType: "new_model",
|
EventType: "new_model",
|
||||||
ModelName: "Claude Opus 4.7",
|
ModelName: "Claude Opus 4.7",
|
||||||
ProviderName: "Anthropic",
|
ProviderName: "Anthropic",
|
||||||
TrustLabel: "聚合来源",
|
TrustLabel: "聚合来源",
|
||||||
Baseline: "首次出现",
|
Baseline: "首次出现",
|
||||||
Summary: "新模型上线。",
|
Summary: "新模型上线。",
|
||||||
SourceKindLabel: "模型快照",
|
SourceKindLabel: "模型快照",
|
||||||
PrimarySource: "OpenRouter / region_pricing",
|
PrimarySource: "OpenRouter / region_pricing",
|
||||||
UpdatedAt: "2026-05-13 09:00",
|
UpdatedAt: "2026-05-13 09:00",
|
||||||
EvidenceDetail: "models.created_at = 今日,且已存在最新价格快照",
|
EvidenceDetail: "models.created_at = 今日,且已存在最新价格快照",
|
||||||
Priority: 70,
|
Priority: 70,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -575,3 +575,24 @@ func TestHeadlineItemFromOfficialReleaseEvent(t *testing.T) {
|
|||||||
t.Fatalf("expected primary source to be preserved, got %+v", item)
|
t.Fatalf("expected primary source to be preserved, 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -29,6 +29,8 @@ type ModelPricing struct {
|
|||||||
SourceURL string
|
SourceURL string
|
||||||
ModelSourceURL string
|
ModelSourceURL string
|
||||||
ReleaseDate string
|
ReleaseDate string
|
||||||
|
DateConfidence string
|
||||||
|
DateSourceKind string
|
||||||
Modality string
|
Modality string
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -47,6 +49,8 @@ type bytedanceModelMetadata struct {
|
|||||||
Prefix string
|
Prefix string
|
||||||
ReleaseDate string
|
ReleaseDate string
|
||||||
ModelSourceURL string
|
ModelSourceURL string
|
||||||
|
DateConfidence string
|
||||||
|
DateSourceKind string
|
||||||
}
|
}
|
||||||
|
|
||||||
var bytedanceModelMetadataRules = []bytedanceModelMetadata{
|
var bytedanceModelMetadataRules = []bytedanceModelMetadata{
|
||||||
@@ -54,60 +58,85 @@ var bytedanceModelMetadataRules = []bytedanceModelMetadata{
|
|||||||
Prefix: "bytedance-doubao-1.5-vision-pro",
|
Prefix: "bytedance-doubao-1.5-vision-pro",
|
||||||
ReleaseDate: "2025-01-22",
|
ReleaseDate: "2025-01-22",
|
||||||
ModelSourceURL: "https://developer.volcengine.com/articles/7462939272262189083",
|
ModelSourceURL: "https://developer.volcengine.com/articles/7462939272262189083",
|
||||||
|
DateConfidence: "official_primary",
|
||||||
|
DateSourceKind: "official_announcement",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Prefix: "bytedance-doubao-1.5-pro",
|
Prefix: "bytedance-doubao-1.5-pro",
|
||||||
ReleaseDate: "2025-01-22",
|
ReleaseDate: "2025-01-22",
|
||||||
ModelSourceURL: "https://developer.volcengine.com/articles/7462939272262189083",
|
ModelSourceURL: "https://developer.volcengine.com/articles/7462939272262189083",
|
||||||
|
DateConfidence: "official_primary",
|
||||||
|
DateSourceKind: "official_announcement",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Prefix: "bytedance-doubao-1.5-lite",
|
Prefix: "bytedance-doubao-1.5-lite",
|
||||||
ReleaseDate: "2025-01-22",
|
ReleaseDate: "2025-01-22",
|
||||||
ModelSourceURL: "https://developer.volcengine.com/articles/7462939272262189083",
|
ModelSourceURL: "https://developer.volcengine.com/articles/7462939272262189083",
|
||||||
|
DateConfidence: "official_primary",
|
||||||
|
DateSourceKind: "official_announcement",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Prefix: "bytedance-doubao-1.5-thinking",
|
Prefix: "bytedance-doubao-1.5-thinking",
|
||||||
ReleaseDate: "2025-04-17",
|
ReleaseDate: "2025-04-17",
|
||||||
ModelSourceURL: "https://developer.volcengine.com/articles/7496718897794039827",
|
ModelSourceURL: "https://developer.volcengine.com/articles/7496718897794039827",
|
||||||
|
DateConfidence: "official_primary",
|
||||||
|
DateSourceKind: "official_announcement",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Prefix: "bytedance-doubao-seed-1.6",
|
Prefix: "bytedance-doubao-seed-1.6",
|
||||||
ReleaseDate: "2025-06-11",
|
ReleaseDate: "2025-06-11",
|
||||||
ModelSourceURL: "https://developer.volcengine.com/articles/7517188354606104612",
|
ModelSourceURL: "https://developer.volcengine.com/articles/7517188354606104612",
|
||||||
|
DateConfidence: "official_primary",
|
||||||
|
DateSourceKind: "official_announcement",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Prefix: "bytedance-doubao-seed-1.8",
|
Prefix: "bytedance-doubao-seed-1.8",
|
||||||
|
ReleaseDate: "2025-12-18",
|
||||||
ModelSourceURL: "https://developer.volcengine.com/articles/7601918680544641034",
|
ModelSourceURL: "https://developer.volcengine.com/articles/7601918680544641034",
|
||||||
|
DateConfidence: "secondary_authoritative",
|
||||||
|
DateSourceKind: "secondary_authoritative_report",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Prefix: "bytedance-doubao-seed-2.0-code",
|
Prefix: "bytedance-doubao-seed-2.0-code",
|
||||||
ReleaseDate: "2026-02-14",
|
ReleaseDate: "2026-02-14",
|
||||||
ModelSourceURL: "https://developer.volcengine.com/articles/7610285824933445675",
|
ModelSourceURL: "https://developer.volcengine.com/articles/7610285824933445675",
|
||||||
|
DateConfidence: "official_primary",
|
||||||
|
DateSourceKind: "official_announcement",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Prefix: "bytedance-doubao-seed-2.0-pro",
|
Prefix: "bytedance-doubao-seed-2.0-pro",
|
||||||
ReleaseDate: "2026-02-14",
|
ReleaseDate: "2026-02-14",
|
||||||
ModelSourceURL: "https://developer.volcengine.com/articles/7610285824933445675",
|
ModelSourceURL: "https://developer.volcengine.com/articles/7610285824933445675",
|
||||||
|
DateConfidence: "official_primary",
|
||||||
|
DateSourceKind: "official_announcement",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Prefix: "bytedance-doubao-seed-2.0-mini",
|
Prefix: "bytedance-doubao-seed-2.0-mini",
|
||||||
ReleaseDate: "2026-02-14",
|
ReleaseDate: "2026-02-14",
|
||||||
ModelSourceURL: "https://developer.volcengine.com/articles/7610285824933445675",
|
ModelSourceURL: "https://developer.volcengine.com/articles/7610285824933445675",
|
||||||
|
DateConfidence: "official_primary",
|
||||||
|
DateSourceKind: "official_announcement",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Prefix: "bytedance-doubao-seed-2.0-lite",
|
Prefix: "bytedance-doubao-seed-2.0-lite",
|
||||||
ReleaseDate: "2026-02-14",
|
ReleaseDate: "2026-02-14",
|
||||||
ModelSourceURL: "https://developer.volcengine.com/articles/7610285824933445675",
|
ModelSourceURL: "https://developer.volcengine.com/articles/7610285824933445675",
|
||||||
|
DateConfidence: "official_primary",
|
||||||
|
DateSourceKind: "official_announcement",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Prefix: "bytedance-doubao-seed-code",
|
Prefix: "bytedance-doubao-seed-code",
|
||||||
ReleaseDate: "2024-06-26",
|
ReleaseDate: "2024-06-26",
|
||||||
ModelSourceURL: "https://developer.volcengine.com/articles/7383101327527641125",
|
ModelSourceURL: "https://developer.volcengine.com/articles/7383101327527641125",
|
||||||
|
DateConfidence: "official_primary",
|
||||||
|
DateSourceKind: "official_announcement",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Prefix: "bytedance-seedance-1.0-lite",
|
Prefix: "bytedance-seedance-1.0-lite",
|
||||||
ReleaseDate: "2025-05-13",
|
ReleaseDate: "2025-05-13",
|
||||||
ModelSourceURL: "https://developer.volcengine.com/articles/7504284064976502823",
|
ModelSourceURL: "https://developer.volcengine.com/articles/7504284064976502823",
|
||||||
|
DateConfidence: "official_primary",
|
||||||
|
DateSourceKind: "official_announcement",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -121,17 +150,32 @@ func enrichBytedanceModelMetadata(model ModelPricing) ModelPricing {
|
|||||||
if metadata.ModelSourceURL != "" {
|
if metadata.ModelSourceURL != "" {
|
||||||
model.ModelSourceURL = metadata.ModelSourceURL
|
model.ModelSourceURL = metadata.ModelSourceURL
|
||||||
}
|
}
|
||||||
|
if metadata.DateConfidence != "" {
|
||||||
|
model.DateConfidence = metadata.DateConfidence
|
||||||
|
}
|
||||||
|
if metadata.DateSourceKind != "" {
|
||||||
|
model.DateSourceKind = metadata.DateSourceKind
|
||||||
|
}
|
||||||
return model
|
return model
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if model.ModelSourceURL == "" {
|
if model.ModelSourceURL == "" {
|
||||||
model.ModelSourceURL = model.SourceURL
|
model.ModelSourceURL = model.SourceURL
|
||||||
}
|
}
|
||||||
|
if model.DateConfidence == "" {
|
||||||
|
model.DateConfidence = "unknown"
|
||||||
|
}
|
||||||
|
if model.DateSourceKind == "" {
|
||||||
|
model.DateSourceKind = "unknown"
|
||||||
|
}
|
||||||
return model
|
return model
|
||||||
}
|
}
|
||||||
|
|
||||||
func hasExplicitModelMetadata(model ModelPricing) bool {
|
func hasExplicitModelMetadata(model ModelPricing) bool {
|
||||||
return strings.TrimSpace(model.ReleaseDate) != "" || firstNonEmpty(model.ModelSourceURL) != "" && model.ModelSourceURL != model.SourceURL
|
return strings.TrimSpace(model.ReleaseDate) != "" ||
|
||||||
|
firstNonEmpty(model.ModelSourceURL) != "" && model.ModelSourceURL != model.SourceURL ||
|
||||||
|
strings.TrimSpace(model.DateConfidence) != "" && model.DateConfidence != "unknown" ||
|
||||||
|
strings.TrimSpace(model.DateSourceKind) != "" && model.DateSourceKind != "unknown"
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
@@ -222,9 +266,9 @@ func main() {
|
|||||||
err = db.QueryRow("SELECT id FROM models WHERE external_id = $1", p.ModelID).Scan(&modelID)
|
err = db.QueryRow("SELECT id FROM models WHERE external_id = $1", p.ModelID).Scan(&modelID)
|
||||||
if err == sql.ErrNoRows {
|
if err == sql.ErrNoRows {
|
||||||
err = db.QueryRow(
|
err = db.QueryRow(
|
||||||
`INSERT INTO models (external_id, name, provider_id, modality, context_length, status, source, batch_id, source_url, release_date)
|
`INSERT INTO models (external_id, name, provider_id, modality, context_length, status, source, batch_id, source_url, release_date, date_confidence, date_source_kind)
|
||||||
VALUES ($1, $2, $3, $4, $5, 'active', $6, $7, $8, $9) RETURNING id`,
|
VALUES ($1, $2, $3, $4, $5, 'active', $6, $7, $8, $9, $10, $11) RETURNING id`,
|
||||||
p.ModelID, p.ModelName, providerID, p.Modality, p.ContextLength, p.OperatorName, batchID, firstNonEmpty(p.ModelSourceURL, p.SourceURL), releaseDateValue(p.ReleaseDate),
|
p.ModelID, p.ModelName, providerID, p.Modality, p.ContextLength, p.OperatorName, batchID, firstNonEmpty(p.ModelSourceURL, p.SourceURL), releaseDateValue(p.ReleaseDate), p.DateConfidence, p.DateSourceKind,
|
||||||
).Scan(&modelID)
|
).Scan(&modelID)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -238,12 +282,20 @@ func main() {
|
|||||||
ELSE COALESCE(NULLIF(source_url, ''), $2)
|
ELSE COALESCE(NULLIF(source_url, ''), $2)
|
||||||
END,
|
END,
|
||||||
release_date = CASE
|
release_date = CASE
|
||||||
WHEN $4 AND $3::date IS NOT NULL THEN $3::date
|
WHEN $4 THEN $3::date
|
||||||
ELSE COALESCE(release_date, $3::date)
|
ELSE COALESCE(release_date, $3::date)
|
||||||
END,
|
END,
|
||||||
|
date_confidence = CASE
|
||||||
|
WHEN $4 THEN $5
|
||||||
|
ELSE COALESCE(NULLIF(date_confidence, ''), $5, 'unknown')
|
||||||
|
END,
|
||||||
|
date_source_kind = CASE
|
||||||
|
WHEN $4 THEN $6
|
||||||
|
ELSE COALESCE(NULLIF(date_source_kind, ''), $6, 'unknown')
|
||||||
|
END,
|
||||||
updated_at = CURRENT_TIMESTAMP
|
updated_at = CURRENT_TIMESTAMP
|
||||||
WHERE id = $1`,
|
WHERE id = $1`,
|
||||||
modelID, firstNonEmpty(p.ModelSourceURL, p.SourceURL), releaseDateValue(p.ReleaseDate), hasExplicitModelMetadata(p),
|
modelID, firstNonEmpty(p.ModelSourceURL, p.SourceURL), releaseDateValue(p.ReleaseDate), hasExplicitModelMetadata(p), p.DateConfidence, p.DateSourceKind,
|
||||||
); err != nil {
|
); err != nil {
|
||||||
log.Printf("Model metadata update error for %s: %v", p.ModelID, err)
|
log.Printf("Model metadata update error for %s: %v", p.ModelID, err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,36 +9,50 @@ func TestEnrichBytedanceModelMetadataUsesSpecificFamilyRules(t *testing.T) {
|
|||||||
modelID string
|
modelID string
|
||||||
wantReleaseDate string
|
wantReleaseDate string
|
||||||
wantSourceURL string
|
wantSourceURL string
|
||||||
|
wantConfidence string
|
||||||
|
wantSourceKind string
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
modelID: "bytedance-doubao-1.5-pro-32k",
|
modelID: "bytedance-doubao-1.5-pro-32k",
|
||||||
wantReleaseDate: "2025-01-22",
|
wantReleaseDate: "2025-01-22",
|
||||||
wantSourceURL: "https://developer.volcengine.com/articles/7462939272262189083",
|
wantSourceURL: "https://developer.volcengine.com/articles/7462939272262189083",
|
||||||
|
wantConfidence: "official_primary",
|
||||||
|
wantSourceKind: "official_announcement",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
modelID: "bytedance-doubao-1.5-vision-pro",
|
modelID: "bytedance-doubao-1.5-vision-pro",
|
||||||
wantReleaseDate: "2025-01-22",
|
wantReleaseDate: "2025-01-22",
|
||||||
wantSourceURL: "https://developer.volcengine.com/articles/7462939272262189083",
|
wantSourceURL: "https://developer.volcengine.com/articles/7462939272262189083",
|
||||||
|
wantConfidence: "official_primary",
|
||||||
|
wantSourceKind: "official_announcement",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
modelID: "bytedance-doubao-seed-1.6-thinking",
|
modelID: "bytedance-doubao-seed-1.6-thinking",
|
||||||
wantReleaseDate: "2025-06-11",
|
wantReleaseDate: "2025-06-11",
|
||||||
wantSourceURL: "https://developer.volcengine.com/articles/7517188354606104612",
|
wantSourceURL: "https://developer.volcengine.com/articles/7517188354606104612",
|
||||||
|
wantConfidence: "official_primary",
|
||||||
|
wantSourceKind: "official_announcement",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
modelID: "bytedance-doubao-1.5-thinking-pro",
|
modelID: "bytedance-doubao-1.5-thinking-pro",
|
||||||
wantReleaseDate: "2025-04-17",
|
wantReleaseDate: "2025-04-17",
|
||||||
wantSourceURL: "https://developer.volcengine.com/articles/7496718897794039827",
|
wantSourceURL: "https://developer.volcengine.com/articles/7496718897794039827",
|
||||||
|
wantConfidence: "official_primary",
|
||||||
|
wantSourceKind: "official_announcement",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
modelID: "bytedance-seedance-1.0-lite",
|
modelID: "bytedance-seedance-1.0-lite",
|
||||||
wantReleaseDate: "2025-05-13",
|
wantReleaseDate: "2025-05-13",
|
||||||
wantSourceURL: "https://developer.volcengine.com/articles/7504284064976502823",
|
wantSourceURL: "https://developer.volcengine.com/articles/7504284064976502823",
|
||||||
|
wantConfidence: "official_primary",
|
||||||
|
wantSourceKind: "official_announcement",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
modelID: "bytedance-doubao-seed-code-256k",
|
modelID: "bytedance-doubao-seed-code-256k",
|
||||||
wantReleaseDate: "2024-06-26",
|
wantReleaseDate: "2024-06-26",
|
||||||
wantSourceURL: "https://developer.volcengine.com/articles/7383101327527641125",
|
wantSourceURL: "https://developer.volcengine.com/articles/7383101327527641125",
|
||||||
|
wantConfidence: "official_primary",
|
||||||
|
wantSourceKind: "official_announcement",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -54,6 +68,12 @@ func TestEnrichBytedanceModelMetadataUsesSpecificFamilyRules(t *testing.T) {
|
|||||||
if enriched.ModelSourceURL != tc.wantSourceURL {
|
if enriched.ModelSourceURL != tc.wantSourceURL {
|
||||||
t.Fatalf("%s source url = %q, want %q", tc.modelID, enriched.ModelSourceURL, tc.wantSourceURL)
|
t.Fatalf("%s source url = %q, want %q", tc.modelID, enriched.ModelSourceURL, tc.wantSourceURL)
|
||||||
}
|
}
|
||||||
|
if enriched.DateConfidence != tc.wantConfidence {
|
||||||
|
t.Fatalf("%s date confidence = %q, want %q", tc.modelID, enriched.DateConfidence, tc.wantConfidence)
|
||||||
|
}
|
||||||
|
if enriched.DateSourceKind != tc.wantSourceKind {
|
||||||
|
t.Fatalf("%s date source kind = %q, want %q", tc.modelID, enriched.DateSourceKind, tc.wantSourceKind)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -69,6 +89,9 @@ func TestEnrichBytedanceModelMetadataFallsBackToPricingSource(t *testing.T) {
|
|||||||
if enriched.ModelSourceURL != "https://www.volcengine.com/docs/82379/1099320" {
|
if enriched.ModelSourceURL != "https://www.volcengine.com/docs/82379/1099320" {
|
||||||
t.Fatalf("model source url = %q, want pricing source fallback", enriched.ModelSourceURL)
|
t.Fatalf("model source url = %q, want pricing source fallback", enriched.ModelSourceURL)
|
||||||
}
|
}
|
||||||
|
if enriched.DateConfidence != "unknown" || enriched.DateSourceKind != "unknown" {
|
||||||
|
t.Fatalf("unexpected fallback date metadata: confidence=%q kind=%q", enriched.DateConfidence, enriched.DateSourceKind)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestEnrichBytedanceModelMetadataSupportsSourceOnlyRules(t *testing.T) {
|
func TestEnrichBytedanceModelMetadataSupportsSourceOnlyRules(t *testing.T) {
|
||||||
@@ -77,12 +100,15 @@ func TestEnrichBytedanceModelMetadataSupportsSourceOnlyRules(t *testing.T) {
|
|||||||
SourceURL: "https://www.volcengine.com/docs/82379/1099320",
|
SourceURL: "https://www.volcengine.com/docs/82379/1099320",
|
||||||
})
|
})
|
||||||
|
|
||||||
if enriched.ReleaseDate != "" {
|
if enriched.ReleaseDate != "2025-12-18" {
|
||||||
t.Fatalf("unexpected release date: %q", enriched.ReleaseDate)
|
t.Fatalf("release date = %q, want %q", enriched.ReleaseDate, "2025-12-18")
|
||||||
}
|
}
|
||||||
if enriched.ModelSourceURL != "https://developer.volcengine.com/articles/7601918680544641034" {
|
if enriched.ModelSourceURL != "https://developer.volcengine.com/articles/7601918680544641034" {
|
||||||
t.Fatalf("model source url = %q, want 1.8 source", enriched.ModelSourceURL)
|
t.Fatalf("model source url = %q, want 1.8 source", enriched.ModelSourceURL)
|
||||||
}
|
}
|
||||||
|
if enriched.DateConfidence != "secondary_authoritative" || enriched.DateSourceKind != "secondary_authoritative_report" {
|
||||||
|
t.Fatalf("unexpected 1.8 date metadata: confidence=%q kind=%q", enriched.DateConfidence, enriched.DateSourceKind)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestEnrichBytedanceModelMetadataUsesTwoPointZeroReleaseDate(t *testing.T) {
|
func TestEnrichBytedanceModelMetadataUsesTwoPointZeroReleaseDate(t *testing.T) {
|
||||||
@@ -97,6 +123,9 @@ func TestEnrichBytedanceModelMetadataUsesTwoPointZeroReleaseDate(t *testing.T) {
|
|||||||
if enriched.ModelSourceURL != "https://developer.volcengine.com/articles/7610285824933445675" {
|
if enriched.ModelSourceURL != "https://developer.volcengine.com/articles/7610285824933445675" {
|
||||||
t.Fatalf("model source url = %q, want 2.0 source", enriched.ModelSourceURL)
|
t.Fatalf("model source url = %q, want 2.0 source", enriched.ModelSourceURL)
|
||||||
}
|
}
|
||||||
|
if enriched.DateConfidence != "official_primary" || enriched.DateSourceKind != "official_announcement" {
|
||||||
|
t.Fatalf("unexpected 2.0 date metadata: confidence=%q kind=%q", enriched.DateConfidence, enriched.DateSourceKind)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestBytedanceReleaseDateValueReturnsNilForUnknownDate(t *testing.T) {
|
func TestBytedanceReleaseDateValueReturnsNilForUnknownDate(t *testing.T) {
|
||||||
|
|||||||
@@ -51,6 +51,8 @@ type ModelPricing struct {
|
|||||||
SourceURL string
|
SourceURL string
|
||||||
ModelSourceURL string
|
ModelSourceURL string
|
||||||
ReleaseDate string
|
ReleaseDate string
|
||||||
|
DateConfidence string
|
||||||
|
DateSourceKind string
|
||||||
Modality string
|
Modality string
|
||||||
SceneTags []string
|
SceneTags []string
|
||||||
}
|
}
|
||||||
@@ -70,6 +72,8 @@ type baiduModelMetadata struct {
|
|||||||
Prefix string
|
Prefix string
|
||||||
ReleaseDate string
|
ReleaseDate string
|
||||||
ModelSourceURL string
|
ModelSourceURL string
|
||||||
|
DateConfidence string
|
||||||
|
DateSourceKind string
|
||||||
}
|
}
|
||||||
|
|
||||||
var baiduModelMetadataRules = []baiduModelMetadata{
|
var baiduModelMetadataRules = []baiduModelMetadata{
|
||||||
@@ -77,54 +81,76 @@ var baiduModelMetadataRules = []baiduModelMetadata{
|
|||||||
Prefix: "baidu-ernie-5.0",
|
Prefix: "baidu-ernie-5.0",
|
||||||
ReleaseDate: "2026-01-22",
|
ReleaseDate: "2026-01-22",
|
||||||
ModelSourceURL: "https://cloud.baidu.com/news/news_eacd0f0b-0ca3-4963-aec8-5e6b9ebef9ba",
|
ModelSourceURL: "https://cloud.baidu.com/news/news_eacd0f0b-0ca3-4963-aec8-5e6b9ebef9ba",
|
||||||
|
DateConfidence: "official_primary",
|
||||||
|
DateSourceKind: "official_announcement",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Prefix: "baidu-ernie-x1.1",
|
Prefix: "baidu-ernie-x1.1",
|
||||||
ReleaseDate: "2025-09-09",
|
ReleaseDate: "2025-09-09",
|
||||||
ModelSourceURL: "https://cloud.baidu.com/news/news_be713ff4-8477-4852-88f1-9cc56c406d6a",
|
ModelSourceURL: "https://cloud.baidu.com/news/news_be713ff4-8477-4852-88f1-9cc56c406d6a",
|
||||||
|
DateConfidence: "official_primary",
|
||||||
|
DateSourceKind: "official_announcement",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Prefix: "baidu-ernie-5.1",
|
Prefix: "baidu-ernie-5.1",
|
||||||
ModelSourceURL: "https://cloud.baidu.com/product/wenxinworkshop.html",
|
ModelSourceURL: "https://cloud.baidu.com/product/wenxinworkshop.html",
|
||||||
|
DateConfidence: "unknown",
|
||||||
|
DateSourceKind: "official_product_page",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Prefix: "baidu-ernie-4.5-turbo-vl",
|
Prefix: "baidu-ernie-4.5-turbo-vl",
|
||||||
ModelSourceURL: "https://cloud.baidu.com/product/wenxinworkshop.html",
|
ModelSourceURL: "https://cloud.baidu.com/product/wenxinworkshop.html",
|
||||||
|
DateConfidence: "unknown",
|
||||||
|
DateSourceKind: "official_product_page",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Prefix: "baidu-ernie-4.5-turbo",
|
Prefix: "baidu-ernie-4.5-turbo",
|
||||||
ReleaseDate: "2025-04-25",
|
ReleaseDate: "2025-04-25",
|
||||||
ModelSourceURL: "https://cloud.baidu.com/article/3887765",
|
ModelSourceURL: "https://cloud.baidu.com/article/3887765",
|
||||||
|
DateConfidence: "official_primary",
|
||||||
|
DateSourceKind: "official_announcement",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Prefix: "baidu-ernie-x1-turbo",
|
Prefix: "baidu-ernie-x1-turbo",
|
||||||
ReleaseDate: "2025-04-25",
|
ReleaseDate: "2025-04-25",
|
||||||
ModelSourceURL: "https://cloud.baidu.com/article/3887765",
|
ModelSourceURL: "https://cloud.baidu.com/article/3887765",
|
||||||
|
DateConfidence: "official_primary",
|
||||||
|
DateSourceKind: "official_announcement",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Prefix: "baidu-ernie-4.5",
|
Prefix: "baidu-ernie-4.5",
|
||||||
ReleaseDate: "2025-03-16",
|
ReleaseDate: "2025-03-16",
|
||||||
ModelSourceURL: "https://cloud.baidu.com/article/3835921",
|
ModelSourceURL: "https://cloud.baidu.com/article/3835921",
|
||||||
|
DateConfidence: "official_primary",
|
||||||
|
DateSourceKind: "official_announcement",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Prefix: "baidu-ernie-x1",
|
Prefix: "baidu-ernie-x1",
|
||||||
ReleaseDate: "2025-03-16",
|
ReleaseDate: "2025-03-16",
|
||||||
ModelSourceURL: "https://cloud.baidu.com/article/3835921",
|
ModelSourceURL: "https://cloud.baidu.com/article/3835921",
|
||||||
|
DateConfidence: "official_primary",
|
||||||
|
DateSourceKind: "official_announcement",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Prefix: "baidu-ernie-character",
|
Prefix: "baidu-ernie-character",
|
||||||
ReleaseDate: "2024-03-22",
|
ReleaseDate: "2024-03-22",
|
||||||
ModelSourceURL: "https://cloud.baidu.com/news/news_667c065f-0bd7-475d-98c2-901763d0ee77",
|
ModelSourceURL: "https://cloud.baidu.com/news/news_667c065f-0bd7-475d-98c2-901763d0ee77",
|
||||||
|
DateConfidence: "official_primary",
|
||||||
|
DateSourceKind: "official_announcement",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Prefix: "baidu-ernie-lite-pro",
|
Prefix: "baidu-ernie-lite-pro",
|
||||||
ReleaseDate: "2024-03-22",
|
ReleaseDate: "2024-03-22",
|
||||||
ModelSourceURL: "https://cloud.baidu.com/news/news_667c065f-0bd7-475d-98c2-901763d0ee77",
|
ModelSourceURL: "https://cloud.baidu.com/news/news_667c065f-0bd7-475d-98c2-901763d0ee77",
|
||||||
|
DateConfidence: "official_primary",
|
||||||
|
DateSourceKind: "official_announcement",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Prefix: "baidu-ernie-speed-pro",
|
Prefix: "baidu-ernie-speed-pro",
|
||||||
ReleaseDate: "2024-03-22",
|
ReleaseDate: "2024-03-22",
|
||||||
ModelSourceURL: "https://cloud.baidu.com/news/news_667c065f-0bd7-475d-98c2-901763d0ee77",
|
ModelSourceURL: "https://cloud.baidu.com/news/news_667c065f-0bd7-475d-98c2-901763d0ee77",
|
||||||
|
DateConfidence: "official_primary",
|
||||||
|
DateSourceKind: "official_announcement",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -138,17 +164,32 @@ func enrichBaiduModelMetadata(model ModelPricing) ModelPricing {
|
|||||||
if metadata.ModelSourceURL != "" {
|
if metadata.ModelSourceURL != "" {
|
||||||
model.ModelSourceURL = metadata.ModelSourceURL
|
model.ModelSourceURL = metadata.ModelSourceURL
|
||||||
}
|
}
|
||||||
|
if metadata.DateConfidence != "" {
|
||||||
|
model.DateConfidence = metadata.DateConfidence
|
||||||
|
}
|
||||||
|
if metadata.DateSourceKind != "" {
|
||||||
|
model.DateSourceKind = metadata.DateSourceKind
|
||||||
|
}
|
||||||
return model
|
return model
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if model.ModelSourceURL == "" {
|
if model.ModelSourceURL == "" {
|
||||||
model.ModelSourceURL = model.SourceURL
|
model.ModelSourceURL = model.SourceURL
|
||||||
}
|
}
|
||||||
|
if model.DateConfidence == "" {
|
||||||
|
model.DateConfidence = "unknown"
|
||||||
|
}
|
||||||
|
if model.DateSourceKind == "" {
|
||||||
|
model.DateSourceKind = "unknown"
|
||||||
|
}
|
||||||
return model
|
return model
|
||||||
}
|
}
|
||||||
|
|
||||||
func hasExplicitModelMetadata(model ModelPricing) bool {
|
func hasExplicitModelMetadata(model ModelPricing) bool {
|
||||||
return strings.TrimSpace(model.ReleaseDate) != "" || firstNonEmpty(model.ModelSourceURL) != "" && model.ModelSourceURL != model.SourceURL
|
return strings.TrimSpace(model.ReleaseDate) != "" ||
|
||||||
|
firstNonEmpty(model.ModelSourceURL) != "" && model.ModelSourceURL != model.SourceURL ||
|
||||||
|
strings.TrimSpace(model.DateConfidence) != "" && model.DateConfidence != "unknown" ||
|
||||||
|
strings.TrimSpace(model.DateSourceKind) != "" && model.DateSourceKind != "unknown"
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseZhipuPrice(s string) float64 {
|
func parseZhipuPrice(s string) float64 {
|
||||||
@@ -288,9 +329,9 @@ func main() {
|
|||||||
err = db.QueryRow("SELECT id FROM models WHERE external_id = $1", p.ModelID).Scan(&modelID)
|
err = db.QueryRow("SELECT id FROM models WHERE external_id = $1", p.ModelID).Scan(&modelID)
|
||||||
if err == sql.ErrNoRows {
|
if err == sql.ErrNoRows {
|
||||||
err = db.QueryRow(
|
err = db.QueryRow(
|
||||||
`INSERT INTO models (external_id, name, provider_id, modality, context_length, status, source, batch_id, source_url, release_date)
|
`INSERT INTO models (external_id, name, provider_id, modality, context_length, status, source, batch_id, source_url, release_date, date_confidence, date_source_kind)
|
||||||
VALUES ($1, $2, $3, $4, $5, 'active', $6, $7, $8, $9) RETURNING id`,
|
VALUES ($1, $2, $3, $4, $5, 'active', $6, $7, $8, $9, $10, $11) RETURNING id`,
|
||||||
p.ModelID, p.ModelName, providerID, p.Modality, p.ContextLength, p.OperatorName, batchID, firstNonEmpty(p.ModelSourceURL, p.SourceURL), releaseDateValue(p.ReleaseDate),
|
p.ModelID, p.ModelName, providerID, p.Modality, p.ContextLength, p.OperatorName, batchID, firstNonEmpty(p.ModelSourceURL, p.SourceURL), releaseDateValue(p.ReleaseDate), p.DateConfidence, p.DateSourceKind,
|
||||||
).Scan(&modelID)
|
).Scan(&modelID)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -304,12 +345,20 @@ func main() {
|
|||||||
ELSE COALESCE(NULLIF(source_url, ''), $2)
|
ELSE COALESCE(NULLIF(source_url, ''), $2)
|
||||||
END,
|
END,
|
||||||
release_date = CASE
|
release_date = CASE
|
||||||
WHEN $4 AND $3::date IS NOT NULL THEN $3::date
|
WHEN $4 THEN $3::date
|
||||||
ELSE COALESCE(release_date, $3::date)
|
ELSE COALESCE(release_date, $3::date)
|
||||||
END,
|
END,
|
||||||
|
date_confidence = CASE
|
||||||
|
WHEN $4 THEN $5
|
||||||
|
ELSE COALESCE(NULLIF(date_confidence, ''), $5, 'unknown')
|
||||||
|
END,
|
||||||
|
date_source_kind = CASE
|
||||||
|
WHEN $4 THEN $6
|
||||||
|
ELSE COALESCE(NULLIF(date_source_kind, ''), $6, 'unknown')
|
||||||
|
END,
|
||||||
updated_at = CURRENT_TIMESTAMP
|
updated_at = CURRENT_TIMESTAMP
|
||||||
WHERE id = $1`,
|
WHERE id = $1`,
|
||||||
modelID, firstNonEmpty(p.ModelSourceURL, p.SourceURL), releaseDateValue(p.ReleaseDate), hasExplicitModelMetadata(p),
|
modelID, firstNonEmpty(p.ModelSourceURL, p.SourceURL), releaseDateValue(p.ReleaseDate), hasExplicitModelMetadata(p), p.DateConfidence, p.DateSourceKind,
|
||||||
); err != nil {
|
); err != nil {
|
||||||
log.Printf("Model metadata update error for %s: %v", p.ModelID, err)
|
log.Printf("Model metadata update error for %s: %v", p.ModelID, err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,41 +9,57 @@ func TestEnrichBaiduModelMetadataUsesSpecificFamilyRules(t *testing.T) {
|
|||||||
modelID string
|
modelID string
|
||||||
wantReleaseDate string
|
wantReleaseDate string
|
||||||
wantSourceURL string
|
wantSourceURL string
|
||||||
|
wantConfidence string
|
||||||
|
wantSourceKind string
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
modelID: "baidu-ernie-5.0",
|
modelID: "baidu-ernie-5.0",
|
||||||
wantReleaseDate: "2026-01-22",
|
wantReleaseDate: "2026-01-22",
|
||||||
wantSourceURL: "https://cloud.baidu.com/news/news_eacd0f0b-0ca3-4963-aec8-5e6b9ebef9ba",
|
wantSourceURL: "https://cloud.baidu.com/news/news_eacd0f0b-0ca3-4963-aec8-5e6b9ebef9ba",
|
||||||
|
wantConfidence: "official_primary",
|
||||||
|
wantSourceKind: "official_announcement",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
modelID: "baidu-ernie-x1.1",
|
modelID: "baidu-ernie-x1.1",
|
||||||
wantReleaseDate: "2025-09-09",
|
wantReleaseDate: "2025-09-09",
|
||||||
wantSourceURL: "https://cloud.baidu.com/news/news_be713ff4-8477-4852-88f1-9cc56c406d6a",
|
wantSourceURL: "https://cloud.baidu.com/news/news_be713ff4-8477-4852-88f1-9cc56c406d6a",
|
||||||
|
wantConfidence: "official_primary",
|
||||||
|
wantSourceKind: "official_announcement",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
modelID: "baidu-ernie-4.5-turbo-128k",
|
modelID: "baidu-ernie-4.5-turbo-128k",
|
||||||
wantReleaseDate: "2025-04-25",
|
wantReleaseDate: "2025-04-25",
|
||||||
wantSourceURL: "https://cloud.baidu.com/article/3887765",
|
wantSourceURL: "https://cloud.baidu.com/article/3887765",
|
||||||
|
wantConfidence: "official_primary",
|
||||||
|
wantSourceKind: "official_announcement",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
modelID: "baidu-ernie-x1-turbo-32k",
|
modelID: "baidu-ernie-x1-turbo-32k",
|
||||||
wantReleaseDate: "2025-04-25",
|
wantReleaseDate: "2025-04-25",
|
||||||
wantSourceURL: "https://cloud.baidu.com/article/3887765",
|
wantSourceURL: "https://cloud.baidu.com/article/3887765",
|
||||||
|
wantConfidence: "official_primary",
|
||||||
|
wantSourceKind: "official_announcement",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
modelID: "baidu-ernie-4.5-8k",
|
modelID: "baidu-ernie-4.5-8k",
|
||||||
wantReleaseDate: "2025-03-16",
|
wantReleaseDate: "2025-03-16",
|
||||||
wantSourceURL: "https://cloud.baidu.com/article/3835921",
|
wantSourceURL: "https://cloud.baidu.com/article/3835921",
|
||||||
|
wantConfidence: "official_primary",
|
||||||
|
wantSourceKind: "official_announcement",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
modelID: "baidu-ernie-x1-8k",
|
modelID: "baidu-ernie-x1-8k",
|
||||||
wantReleaseDate: "2025-03-16",
|
wantReleaseDate: "2025-03-16",
|
||||||
wantSourceURL: "https://cloud.baidu.com/article/3835921",
|
wantSourceURL: "https://cloud.baidu.com/article/3835921",
|
||||||
|
wantConfidence: "official_primary",
|
||||||
|
wantSourceKind: "official_announcement",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
modelID: "baidu-ernie-character",
|
modelID: "baidu-ernie-character",
|
||||||
wantReleaseDate: "2024-03-22",
|
wantReleaseDate: "2024-03-22",
|
||||||
wantSourceURL: "https://cloud.baidu.com/news/news_667c065f-0bd7-475d-98c2-901763d0ee77",
|
wantSourceURL: "https://cloud.baidu.com/news/news_667c065f-0bd7-475d-98c2-901763d0ee77",
|
||||||
|
wantConfidence: "official_primary",
|
||||||
|
wantSourceKind: "official_announcement",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -59,6 +75,12 @@ func TestEnrichBaiduModelMetadataUsesSpecificFamilyRules(t *testing.T) {
|
|||||||
if enriched.ModelSourceURL != tc.wantSourceURL {
|
if enriched.ModelSourceURL != tc.wantSourceURL {
|
||||||
t.Fatalf("%s source url = %q, want %q", tc.modelID, enriched.ModelSourceURL, tc.wantSourceURL)
|
t.Fatalf("%s source url = %q, want %q", tc.modelID, enriched.ModelSourceURL, tc.wantSourceURL)
|
||||||
}
|
}
|
||||||
|
if enriched.DateConfidence != tc.wantConfidence {
|
||||||
|
t.Fatalf("%s date confidence = %q, want %q", tc.modelID, enriched.DateConfidence, tc.wantConfidence)
|
||||||
|
}
|
||||||
|
if enriched.DateSourceKind != tc.wantSourceKind {
|
||||||
|
t.Fatalf("%s date source kind = %q, want %q", tc.modelID, enriched.DateSourceKind, tc.wantSourceKind)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -74,6 +96,9 @@ func TestEnrichBaiduModelMetadataFallsBackToPricingSource(t *testing.T) {
|
|||||||
if enriched.ModelSourceURL != "https://cloud.baidu.com/doc/qianfan/s/wmh4sv6ya" {
|
if enriched.ModelSourceURL != "https://cloud.baidu.com/doc/qianfan/s/wmh4sv6ya" {
|
||||||
t.Fatalf("model source url = %q, want pricing source fallback", enriched.ModelSourceURL)
|
t.Fatalf("model source url = %q, want pricing source fallback", enriched.ModelSourceURL)
|
||||||
}
|
}
|
||||||
|
if enriched.DateConfidence != "unknown" || enriched.DateSourceKind != "unknown" {
|
||||||
|
t.Fatalf("unexpected fallback date metadata: confidence=%q kind=%q", enriched.DateConfidence, enriched.DateSourceKind)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestEnrichBaiduModelMetadataSupportsSourceOnlyRules(t *testing.T) {
|
func TestEnrichBaiduModelMetadataSupportsSourceOnlyRules(t *testing.T) {
|
||||||
@@ -88,6 +113,9 @@ func TestEnrichBaiduModelMetadataSupportsSourceOnlyRules(t *testing.T) {
|
|||||||
if enriched.ModelSourceURL != "https://cloud.baidu.com/product/wenxinworkshop.html" {
|
if enriched.ModelSourceURL != "https://cloud.baidu.com/product/wenxinworkshop.html" {
|
||||||
t.Fatalf("model source url = %q, want product source", enriched.ModelSourceURL)
|
t.Fatalf("model source url = %q, want product source", enriched.ModelSourceURL)
|
||||||
}
|
}
|
||||||
|
if enriched.DateConfidence != "unknown" || enriched.DateSourceKind != "official_product_page" {
|
||||||
|
t.Fatalf("unexpected source-only metadata: confidence=%q kind=%q", enriched.DateConfidence, enriched.DateSourceKind)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestBaiduReleaseDateValueReturnsNilForUnknownDate(t *testing.T) {
|
func TestBaiduReleaseDateValueReturnsNilForUnknownDate(t *testing.T) {
|
||||||
|
|||||||
@@ -28,6 +28,8 @@ type ModelPricing struct {
|
|||||||
SourceURL string
|
SourceURL string
|
||||||
ModelSourceURL string
|
ModelSourceURL string
|
||||||
ReleaseDate string
|
ReleaseDate string
|
||||||
|
DateConfidence string
|
||||||
|
DateSourceKind string
|
||||||
Modality string
|
Modality string
|
||||||
SceneTags []string
|
SceneTags []string
|
||||||
}
|
}
|
||||||
@@ -47,6 +49,8 @@ type zhipuModelMetadata struct {
|
|||||||
Prefix string
|
Prefix string
|
||||||
ReleaseDate string
|
ReleaseDate string
|
||||||
ModelSourceURL string
|
ModelSourceURL string
|
||||||
|
DateConfidence string
|
||||||
|
DateSourceKind string
|
||||||
}
|
}
|
||||||
|
|
||||||
var zhipuModelMetadataRules = []zhipuModelMetadata{
|
var zhipuModelMetadataRules = []zhipuModelMetadata{
|
||||||
@@ -54,36 +58,50 @@ var zhipuModelMetadataRules = []zhipuModelMetadata{
|
|||||||
Prefix: "glm-5-turbo",
|
Prefix: "glm-5-turbo",
|
||||||
ReleaseDate: "2026-03-15",
|
ReleaseDate: "2026-03-15",
|
||||||
ModelSourceURL: "https://www.zhipuai.cn/en/research/155",
|
ModelSourceURL: "https://www.zhipuai.cn/en/research/155",
|
||||||
|
DateConfidence: "official_primary",
|
||||||
|
DateSourceKind: "official_announcement",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Prefix: "glm-5.1",
|
Prefix: "glm-5.1",
|
||||||
ReleaseDate: "2026-04-07",
|
ReleaseDate: "2026-04-07",
|
||||||
ModelSourceURL: "https://www.zhipuai.cn/zh/research",
|
ModelSourceURL: "https://www.zhipuai.cn/zh/research",
|
||||||
|
DateConfidence: "official_primary",
|
||||||
|
DateSourceKind: "official_announcement",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Prefix: "glm-5",
|
Prefix: "glm-5",
|
||||||
ReleaseDate: "2026-02-11",
|
ReleaseDate: "2026-02-11",
|
||||||
ModelSourceURL: "https://www.zhipuai.cn/zh/research/154",
|
ModelSourceURL: "https://www.zhipuai.cn/zh/research/154",
|
||||||
|
DateConfidence: "official_primary",
|
||||||
|
DateSourceKind: "official_announcement",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Prefix: "glm-4.7-flash",
|
Prefix: "glm-4.7-flash",
|
||||||
ReleaseDate: "2026-01-19",
|
ReleaseDate: "2026-01-19",
|
||||||
ModelSourceURL: "https://www.zhipuai.cn/zh/news/148",
|
ModelSourceURL: "https://www.zhipuai.cn/zh/news/148",
|
||||||
|
DateConfidence: "official_primary",
|
||||||
|
DateSourceKind: "official_announcement",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Prefix: "glm-4.7",
|
Prefix: "glm-4.7",
|
||||||
ReleaseDate: "2025-12-21",
|
ReleaseDate: "2025-12-21",
|
||||||
ModelSourceURL: "https://www.zhipuai.cn/zh/research",
|
ModelSourceURL: "https://www.zhipuai.cn/zh/research",
|
||||||
|
DateConfidence: "official_primary",
|
||||||
|
DateSourceKind: "official_announcement",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Prefix: "glm-4.6v",
|
Prefix: "glm-4.6v",
|
||||||
ReleaseDate: "2025-12-07",
|
ReleaseDate: "2025-12-07",
|
||||||
ModelSourceURL: "https://www.zhipuai.cn/zh/research/144",
|
ModelSourceURL: "https://www.zhipuai.cn/zh/research/144",
|
||||||
|
DateConfidence: "official_primary",
|
||||||
|
DateSourceKind: "official_announcement",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Prefix: "glm-tts",
|
Prefix: "glm-tts",
|
||||||
ReleaseDate: "2025-12-10",
|
ReleaseDate: "2025-12-10",
|
||||||
ModelSourceURL: "https://www.zhipuai.cn/zh/research",
|
ModelSourceURL: "https://www.zhipuai.cn/zh/research",
|
||||||
|
DateConfidence: "official_primary",
|
||||||
|
DateSourceKind: "official_announcement",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -96,17 +114,32 @@ func enrichZhipuModelMetadata(model ModelPricing) ModelPricing {
|
|||||||
if metadata.ModelSourceURL != "" {
|
if metadata.ModelSourceURL != "" {
|
||||||
model.ModelSourceURL = metadata.ModelSourceURL
|
model.ModelSourceURL = metadata.ModelSourceURL
|
||||||
}
|
}
|
||||||
|
if metadata.DateConfidence != "" {
|
||||||
|
model.DateConfidence = metadata.DateConfidence
|
||||||
|
}
|
||||||
|
if metadata.DateSourceKind != "" {
|
||||||
|
model.DateSourceKind = metadata.DateSourceKind
|
||||||
|
}
|
||||||
return model
|
return model
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if model.ModelSourceURL == "" {
|
if model.ModelSourceURL == "" {
|
||||||
model.ModelSourceURL = model.SourceURL
|
model.ModelSourceURL = model.SourceURL
|
||||||
}
|
}
|
||||||
|
if model.DateConfidence == "" {
|
||||||
|
model.DateConfidence = "unknown"
|
||||||
|
}
|
||||||
|
if model.DateSourceKind == "" {
|
||||||
|
model.DateSourceKind = "unknown"
|
||||||
|
}
|
||||||
return model
|
return model
|
||||||
}
|
}
|
||||||
|
|
||||||
func hasExplicitModelMetadata(model ModelPricing) bool {
|
func hasExplicitModelMetadata(model ModelPricing) bool {
|
||||||
return strings.TrimSpace(model.ReleaseDate) != "" || firstNonEmpty(model.ModelSourceURL) != "" && model.ModelSourceURL != model.SourceURL
|
return strings.TrimSpace(model.ReleaseDate) != "" ||
|
||||||
|
firstNonEmpty(model.ModelSourceURL) != "" && model.ModelSourceURL != model.SourceURL ||
|
||||||
|
strings.TrimSpace(model.DateConfidence) != "" && model.DateConfidence != "unknown" ||
|
||||||
|
strings.TrimSpace(model.DateSourceKind) != "" && model.DateSourceKind != "unknown"
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
@@ -228,9 +261,9 @@ func main() {
|
|||||||
err = db.QueryRow("SELECT id FROM models WHERE external_id = $1", p.ModelID).Scan(&modelID)
|
err = db.QueryRow("SELECT id FROM models WHERE external_id = $1", p.ModelID).Scan(&modelID)
|
||||||
if err == sql.ErrNoRows {
|
if err == sql.ErrNoRows {
|
||||||
err = db.QueryRow(
|
err = db.QueryRow(
|
||||||
`INSERT INTO models (external_id, name, provider_id, modality, context_length, status, source, batch_id, source_url, release_date)
|
`INSERT INTO models (external_id, name, provider_id, modality, context_length, status, source, batch_id, source_url, release_date, date_confidence, date_source_kind)
|
||||||
VALUES ($1, $2, $3, $4, $5, 'active', $6, $7, $8, $9) RETURNING id`,
|
VALUES ($1, $2, $3, $4, $5, 'active', $6, $7, $8, $9, $10, $11) RETURNING id`,
|
||||||
p.ModelID, p.ModelName, providerID, p.Modality, p.ContextLength, p.OperatorName, batchID, firstNonEmpty(p.ModelSourceURL, p.SourceURL), releaseDateValue(p.ReleaseDate),
|
p.ModelID, p.ModelName, providerID, p.Modality, p.ContextLength, p.OperatorName, batchID, firstNonEmpty(p.ModelSourceURL, p.SourceURL), releaseDateValue(p.ReleaseDate), p.DateConfidence, p.DateSourceKind,
|
||||||
).Scan(&modelID)
|
).Scan(&modelID)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -244,12 +277,20 @@ func main() {
|
|||||||
ELSE COALESCE(NULLIF(source_url, ''), $2)
|
ELSE COALESCE(NULLIF(source_url, ''), $2)
|
||||||
END,
|
END,
|
||||||
release_date = CASE
|
release_date = CASE
|
||||||
WHEN $4 AND $3::date IS NOT NULL THEN $3::date
|
WHEN $4 THEN $3::date
|
||||||
ELSE COALESCE(release_date, $3::date)
|
ELSE COALESCE(release_date, $3::date)
|
||||||
END,
|
END,
|
||||||
|
date_confidence = CASE
|
||||||
|
WHEN $4 THEN $5
|
||||||
|
ELSE COALESCE(NULLIF(date_confidence, ''), $5, 'unknown')
|
||||||
|
END,
|
||||||
|
date_source_kind = CASE
|
||||||
|
WHEN $4 THEN $6
|
||||||
|
ELSE COALESCE(NULLIF(date_source_kind, ''), $6, 'unknown')
|
||||||
|
END,
|
||||||
updated_at = CURRENT_TIMESTAMP
|
updated_at = CURRENT_TIMESTAMP
|
||||||
WHERE id = $1`,
|
WHERE id = $1`,
|
||||||
modelID, firstNonEmpty(p.ModelSourceURL, p.SourceURL), releaseDateValue(p.ReleaseDate), hasExplicitModelMetadata(p),
|
modelID, firstNonEmpty(p.ModelSourceURL, p.SourceURL), releaseDateValue(p.ReleaseDate), hasExplicitModelMetadata(p), p.DateConfidence, p.DateSourceKind,
|
||||||
); err != nil {
|
); err != nil {
|
||||||
log.Printf("Model metadata update error for %s: %v", p.ModelID, err)
|
log.Printf("Model metadata update error for %s: %v", p.ModelID, err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,36 +9,50 @@ func TestEnrichZhipuModelMetadataUsesSpecificFamilyRules(t *testing.T) {
|
|||||||
modelID string
|
modelID string
|
||||||
wantReleaseDate string
|
wantReleaseDate string
|
||||||
wantSourceURL string
|
wantSourceURL string
|
||||||
|
wantConfidence string
|
||||||
|
wantSourceKind string
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
modelID: "glm-5.1-32k",
|
modelID: "glm-5.1-32k",
|
||||||
wantReleaseDate: "2026-04-07",
|
wantReleaseDate: "2026-04-07",
|
||||||
wantSourceURL: "https://www.zhipuai.cn/zh/research",
|
wantSourceURL: "https://www.zhipuai.cn/zh/research",
|
||||||
|
wantConfidence: "official_primary",
|
||||||
|
wantSourceKind: "official_announcement",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
modelID: "glm-5-turbo-32k",
|
modelID: "glm-5-turbo-32k",
|
||||||
wantReleaseDate: "2026-03-15",
|
wantReleaseDate: "2026-03-15",
|
||||||
wantSourceURL: "https://www.zhipuai.cn/en/research/155",
|
wantSourceURL: "https://www.zhipuai.cn/en/research/155",
|
||||||
|
wantConfidence: "official_primary",
|
||||||
|
wantSourceKind: "official_announcement",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
modelID: "glm-5-32k",
|
modelID: "glm-5-32k",
|
||||||
wantReleaseDate: "2026-02-11",
|
wantReleaseDate: "2026-02-11",
|
||||||
wantSourceURL: "https://www.zhipuai.cn/zh/research/154",
|
wantSourceURL: "https://www.zhipuai.cn/zh/research/154",
|
||||||
|
wantConfidence: "official_primary",
|
||||||
|
wantSourceKind: "official_announcement",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
modelID: "glm-4.7-flash",
|
modelID: "glm-4.7-flash",
|
||||||
wantReleaseDate: "2026-01-19",
|
wantReleaseDate: "2026-01-19",
|
||||||
wantSourceURL: "https://www.zhipuai.cn/zh/news/148",
|
wantSourceURL: "https://www.zhipuai.cn/zh/news/148",
|
||||||
|
wantConfidence: "official_primary",
|
||||||
|
wantSourceKind: "official_announcement",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
modelID: "glm-4.6v-flashx",
|
modelID: "glm-4.6v-flashx",
|
||||||
wantReleaseDate: "2025-12-07",
|
wantReleaseDate: "2025-12-07",
|
||||||
wantSourceURL: "https://www.zhipuai.cn/zh/research/144",
|
wantSourceURL: "https://www.zhipuai.cn/zh/research/144",
|
||||||
|
wantConfidence: "official_primary",
|
||||||
|
wantSourceKind: "official_announcement",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
modelID: "glm-tts-clone",
|
modelID: "glm-tts-clone",
|
||||||
wantReleaseDate: "2025-12-10",
|
wantReleaseDate: "2025-12-10",
|
||||||
wantSourceURL: "https://www.zhipuai.cn/zh/research",
|
wantSourceURL: "https://www.zhipuai.cn/zh/research",
|
||||||
|
wantConfidence: "official_primary",
|
||||||
|
wantSourceKind: "official_announcement",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -54,6 +68,12 @@ func TestEnrichZhipuModelMetadataUsesSpecificFamilyRules(t *testing.T) {
|
|||||||
if enriched.ModelSourceURL != tc.wantSourceURL {
|
if enriched.ModelSourceURL != tc.wantSourceURL {
|
||||||
t.Fatalf("%s source url = %q, want %q", tc.modelID, enriched.ModelSourceURL, tc.wantSourceURL)
|
t.Fatalf("%s source url = %q, want %q", tc.modelID, enriched.ModelSourceURL, tc.wantSourceURL)
|
||||||
}
|
}
|
||||||
|
if enriched.DateConfidence != tc.wantConfidence {
|
||||||
|
t.Fatalf("%s date confidence = %q, want %q", tc.modelID, enriched.DateConfidence, tc.wantConfidence)
|
||||||
|
}
|
||||||
|
if enriched.DateSourceKind != tc.wantSourceKind {
|
||||||
|
t.Fatalf("%s date source kind = %q, want %q", tc.modelID, enriched.DateSourceKind, tc.wantSourceKind)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -69,6 +89,9 @@ func TestEnrichZhipuModelMetadataFallsBackToPricingSource(t *testing.T) {
|
|||||||
if enriched.ModelSourceURL != "https://open.bigmodel.cn/pricing" {
|
if enriched.ModelSourceURL != "https://open.bigmodel.cn/pricing" {
|
||||||
t.Fatalf("model source url = %q, want pricing source fallback", enriched.ModelSourceURL)
|
t.Fatalf("model source url = %q, want pricing source fallback", enriched.ModelSourceURL)
|
||||||
}
|
}
|
||||||
|
if enriched.DateConfidence != "unknown" || enriched.DateSourceKind != "unknown" {
|
||||||
|
t.Fatalf("unexpected fallback date metadata: confidence=%q kind=%q", enriched.DateConfidence, enriched.DateSourceKind)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestZhipuReleaseDateValueReturnsNilForUnknownDate(t *testing.T) {
|
func TestZhipuReleaseDateValueReturnsNilForUnknownDate(t *testing.T) {
|
||||||
|
|||||||
@@ -42,5 +42,11 @@ func TestOfficialImportScriptsWriteModelSourceURLAndReleaseDate(t *testing.T) {
|
|||||||
if !strings.Contains(content, "release_date") {
|
if !strings.Contains(content, "release_date") {
|
||||||
t.Fatalf("%s missing release_date in models write path", relativePath)
|
t.Fatalf("%s missing release_date in models write path", relativePath)
|
||||||
}
|
}
|
||||||
|
if !strings.Contains(content, "date_confidence") {
|
||||||
|
t.Fatalf("%s missing date_confidence in models write path", relativePath)
|
||||||
|
}
|
||||||
|
if !strings.Contains(content, "date_source_kind") {
|
||||||
|
t.Fatalf("%s missing date_source_kind in models write path", relativePath)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user