fix(pricing): seed Claude Fable 5.1 / Mythos 5.1 and restore Sonnet 5 to the $2/$10 standard price (#7051)

- Add claude-fable-5-1 / claude-mythos-5-1 pricing rows ($10/$50, cache read $0.25 = 0.025x, cache write $12.50). Without a row these requests were stored at $0: the prefix rule only looks for longer dated ids and cannot fall back to claude-fable-5.
- Sonnet 5: Anthropic's pricing page now states the $2/$10 introductory price is the standard price and the September 1 increase to $3/$15 will not occur. Update the seed and add a repair_current_model_pricing guard so installs seeded at 3/15/0.30/3.75 are corrected; user-customized rows are left alone.
- Tests: seed values for the three rows; the repair guard fixes the old list price and keeps a custom price.

Fixes #7050
This commit is contained in:
Eureka
2026-09-02 12:02:18 +08:00
committed by GitHub
parent b1250dc7ac
commit 460aa8c73d
2 changed files with 167 additions and 5 deletions
+38 -5
View File
@@ -1604,6 +1604,24 @@ impl Database {
/// 注意: model_id 使用短横线格式(如 claude-haiku-4-5),与 API 返回的模型名称标准化后一致
fn seed_model_pricing(conn: &Connection) -> Result<(), AppError> {
let pricing_data = [
// Claude Fable 5.1 / Mythos 5.1(2026-09-01 发布;同 Fable 5 价,
// 但缓存读为 0.025x = $0.25,非 Fable 5 的 $1)
(
"claude-fable-5-1",
"Claude Fable 5.1",
"10",
"50",
"0.25",
"12.50",
),
(
"claude-mythos-5-1",
"Claude Mythos 5.1",
"10",
"50",
"0.25",
"12.50",
),
// Claude Fable 5(Opus 之上的新档)
(
"claude-fable-5",
@@ -1632,14 +1650,15 @@ impl Database {
"0.50",
"6.25",
),
// Claude Sonnet 5(list 价,与 Sonnet 4.6 一致;促销 $2/$10 至 2026-08-31 不入表)
// Claude Sonnet 5(官方定价页 2026-09 确认:$2/$10 介绍价转为正式价,
// 原定 09-01 涨至 $3/$15 取消)
(
"claude-sonnet-5",
"Claude Sonnet 5",
"3",
"15",
"0.30",
"3.75",
"2",
"10",
"0.20",
"2.50",
),
// Claude 4.7 系列
(
@@ -2597,6 +2616,20 @@ impl Database {
fn repair_current_model_pricing(conn: &Connection) -> Result<(), AppError> {
let pricing_fixes = [
// 2026-09-02 官方定价页确认 Sonnet 5 $2/$10 介绍价转为正式价、原定 09-01 涨至
// $3/$15 取消:早先按 list 价 seed 的行改回正式价(用户手改过的行不匹配旧值,不动)
(
"claude-sonnet-5",
"Claude Sonnet 5",
"2",
"10",
"0.20",
"2.50",
"3",
"15",
"0.30",
"3.75",
),
// 2026-08-13 models.dev 审计核价:grok-4.5 的 cached input 官方挂牌为 0.30
// (docs.x.ai 现行价表),与 grok-4.5-build 的实测计费一致;早先按 0.50
// 录入的行在此校正。注意 0.50 是 grok-4.6 的 cached 价,勿两者互串
+129
View File
@@ -892,6 +892,135 @@ fn model_pricing_seed_repairs_known_outdated_builtin_prices() {
assert_eq!(glm, ("9".to_string(), "9".to_string(), "9".to_string()));
}
#[test]
fn model_pricing_seed_includes_claude_5_1_and_standard_sonnet_5_prices() {
let db = Database::memory().expect("create memory db");
let conn = db.conn.lock().expect("lock conn");
for model_id in ["claude-fable-5-1", "claude-mythos-5-1"] {
let price: (String, String, String, String) = conn
.query_row(
"SELECT input_cost_per_million, output_cost_per_million,
cache_read_cost_per_million, cache_creation_cost_per_million
FROM model_pricing WHERE model_id = ?1",
[model_id],
|row| Ok((row.get(0)?, row.get(1)?, row.get(2)?, row.get(3)?)),
)
.expect("query Fable 5.1 family price");
// 缓存读 0.025x = $0.25,不是 Fable 5 的 $1
assert_eq!(
price,
(
"10".to_string(),
"50".to_string(),
"0.25".to_string(),
"12.50".to_string(),
),
"{model_id}"
);
}
let sonnet: (String, String, String, String) = conn
.query_row(
"SELECT input_cost_per_million, output_cost_per_million,
cache_read_cost_per_million, cache_creation_cost_per_million
FROM model_pricing WHERE model_id = 'claude-sonnet-5'",
[],
|row| Ok((row.get(0)?, row.get(1)?, row.get(2)?, row.get(3)?)),
)
.expect("query Sonnet 5 price");
// $2/$10 介绍价已转为正式价(原定 2026-09-01 涨至 $3/$15 取消)
assert_eq!(
sonnet,
(
"2".to_string(),
"10".to_string(),
"0.20".to_string(),
"2.50".to_string(),
)
);
}
#[test]
fn model_pricing_seed_repairs_sonnet_5_list_price_but_keeps_custom_price() {
let db = Database::memory().expect("create memory db");
{
let conn = db.conn.lock().expect("lock conn");
// 旧 seed 按 list 价录入的行 → 应被修正
conn.execute(
"UPDATE model_pricing
SET input_cost_per_million = '3',
output_cost_per_million = '15',
cache_read_cost_per_million = '0.30',
cache_creation_cost_per_million = '3.75'
WHERE model_id = 'claude-sonnet-5'",
[],
)
.expect("restore old Sonnet 5 list price");
}
db.ensure_model_pricing_seeded()
.expect("ensure pricing seeded");
{
let conn = db.conn.lock().expect("lock conn");
let sonnet: (String, String, String, String) = conn
.query_row(
"SELECT input_cost_per_million, output_cost_per_million,
cache_read_cost_per_million, cache_creation_cost_per_million
FROM model_pricing WHERE model_id = 'claude-sonnet-5'",
[],
|row| Ok((row.get(0)?, row.get(1)?, row.get(2)?, row.get(3)?)),
)
.expect("query repaired Sonnet 5 price");
assert_eq!(
sonnet,
(
"2".to_string(),
"10".to_string(),
"0.20".to_string(),
"2.50".to_string(),
)
);
// 用户手改过的价(不匹配旧 seed 值)不动
conn.execute(
"UPDATE model_pricing
SET input_cost_per_million = '9',
output_cost_per_million = '9',
cache_read_cost_per_million = '9',
cache_creation_cost_per_million = '9'
WHERE model_id = 'claude-sonnet-5'",
[],
)
.expect("set custom Sonnet 5 price");
}
db.ensure_model_pricing_seeded()
.expect("ensure pricing seeded again");
let conn = db.conn.lock().expect("lock conn");
let custom: (String, String, String, String) = conn
.query_row(
"SELECT input_cost_per_million, output_cost_per_million,
cache_read_cost_per_million, cache_creation_cost_per_million
FROM model_pricing WHERE model_id = 'claude-sonnet-5'",
[],
|row| Ok((row.get(0)?, row.get(1)?, row.get(2)?, row.get(3)?)),
)
.expect("query custom Sonnet 5 price");
assert_eq!(
custom,
(
"9".to_string(),
"9".to_string(),
"9".to_string(),
"9".to_string(),
)
);
}
#[test]
fn ensure_incremental_auto_vacuum_rebuilds_existing_file_db() {
let temp = NamedTempFile::new().expect("create temp db file");