mirror of
https://github.com/farion1231/cc-switch.git
synced 2026-08-04 11:43:57 +08:00
Update default models and pricing across presets
Bump default model names project-wide: gpt-5.4 -> gpt-5.5,
gemini-3.x -> gemini-3.5-flash, glm-5 -> glm-5.1, and
grok-code-fast-1 -> grok-build-0.1 across all provider presets
(claude, codex, gemini, hermes, openclaw, opencode, universal),
Gemini config, and stream check defaults.
Pricing:
- Seed gemini-3.5-flash, gemini-3.1-flash-lite, step-3.5-flash-2603,
doubao-seed-2.0-code, mimo-v2.5(/pro), qwen3-coder-480b, grok-build-0.1.
- Correct deepseek-v4-flash/pro, glm-5/5.1, grok pricing.
- Add repair_current_model_pricing: idempotent pass that fixes only
rows still equal to the outdated built-in values, preserving any
user-customized prices (seed uses INSERT OR IGNORE and cannot update
existing rows).
Fixes from review:
- opencode: drop duplicate gemini-3.5-flash variant (unreachable via
.find), keep the entry with the full minimal/low/medium/high set.
- Align stale display names/costs to gemini-3.5-flash (hermes, openclaw,
opencode); openclaw cost -> {1.5, 9, 0.15} to match seed.
- i18n (zh/en/ja/zh-TW): refresh OMO category tooltips for new model
names; fix writing tooltip to Kimi K2.5 to match its recommended.
Update tests accordingly and add a regression test asserting unique
model ids in the Google opencode preset variants.
This commit is contained in:
@@ -1485,6 +1485,15 @@ impl Database {
|
||||
("gpt-4.1", "GPT-4.1", "2", "8", "0.50", "0"),
|
||||
("gpt-4.1-mini", "GPT-4.1 Mini", "0.40", "1.60", "0.10", "0"),
|
||||
("gpt-4.1-nano", "GPT-4.1 Nano", "0.10", "0.40", "0.025", "0"),
|
||||
// Gemini 3.5 系列
|
||||
(
|
||||
"gemini-3.5-flash",
|
||||
"Gemini 3.5 Flash",
|
||||
"1.50",
|
||||
"9.00",
|
||||
"0.15",
|
||||
"0",
|
||||
),
|
||||
// Gemini 3.1 系列
|
||||
(
|
||||
"gemini-3.1-pro-preview",
|
||||
@@ -1494,6 +1503,14 @@ impl Database {
|
||||
"0.20",
|
||||
"0",
|
||||
),
|
||||
(
|
||||
"gemini-3.1-flash-lite",
|
||||
"Gemini 3.1 Flash Lite",
|
||||
"0.25",
|
||||
"1.50",
|
||||
"0.025",
|
||||
"0",
|
||||
),
|
||||
(
|
||||
"gemini-3.1-flash-lite-preview",
|
||||
"Gemini 3.1 Flash Lite Preview",
|
||||
@@ -1562,6 +1579,14 @@ impl Database {
|
||||
"0.02",
|
||||
"0",
|
||||
),
|
||||
(
|
||||
"step-3.5-flash-2603",
|
||||
"Step 3.5 Flash 2603",
|
||||
"0.10",
|
||||
"0.30",
|
||||
"0.02",
|
||||
"0",
|
||||
),
|
||||
// ====== 国产模型 (USD/1M tokens) ======
|
||||
// Doubao (字节跳动)
|
||||
(
|
||||
@@ -1588,6 +1613,14 @@ impl Database {
|
||||
"0",
|
||||
"0",
|
||||
),
|
||||
(
|
||||
"doubao-seed-2-0-code-preview-latest",
|
||||
"Doubao Seed 2.0 Code Preview",
|
||||
"0.47",
|
||||
"2.37",
|
||||
"0",
|
||||
"0",
|
||||
),
|
||||
(
|
||||
"doubao-seed-2-0-lite",
|
||||
"Doubao Seed 2.0 Lite",
|
||||
@@ -1644,15 +1677,15 @@ impl Database {
|
||||
"DeepSeek V4 Flash",
|
||||
"0.14",
|
||||
"0.28",
|
||||
"0.028",
|
||||
"0.0028",
|
||||
"0",
|
||||
),
|
||||
(
|
||||
"deepseek-v4-pro",
|
||||
"DeepSeek V4 Pro",
|
||||
"1.68",
|
||||
"3.36",
|
||||
"0.14",
|
||||
"0.435",
|
||||
"0.87",
|
||||
"0.003625",
|
||||
"0",
|
||||
),
|
||||
// Kimi (月之暗面)
|
||||
@@ -1714,8 +1747,8 @@ impl Database {
|
||||
// GLM (智谱)
|
||||
("glm-4.7", "GLM-4.7", "0.39", "1.75", "0.04", "0"),
|
||||
("glm-4.6", "GLM-4.6", "0.28", "1.11", "0.03", "0"),
|
||||
("glm-5", "GLM-5", "0.72", "2.30", "0", "0"),
|
||||
("glm-5.1", "GLM-5.1", "0.95", "3.15", "0", "0"),
|
||||
("glm-5", "GLM-5", "1", "3.2", "0.2", "0"),
|
||||
("glm-5.1", "GLM-5.1", "1.4", "4.4", "0.26", "0"),
|
||||
// MiMo (小米)
|
||||
(
|
||||
"mimo-v2-flash",
|
||||
@@ -1726,6 +1759,8 @@ impl Database {
|
||||
"0",
|
||||
),
|
||||
("mimo-v2-pro", "MiMo V2 Pro", "1", "3", "0", "0"),
|
||||
("mimo-v2.5", "MiMo V2.5", "0.09", "0.29", "0.009", "0"),
|
||||
("mimo-v2.5-pro", "MiMo V2.5 Pro", "1", "3", "0", "0"),
|
||||
// Qwen 系列 (阿里巴巴)
|
||||
("qwen3.6-plus", "Qwen3.6 Plus", "0.325", "1.95", "0", "0"),
|
||||
("qwen3.5-plus", "Qwen3.5 Plus", "0.26", "1.56", "0", "0"),
|
||||
@@ -1746,6 +1781,22 @@ impl Database {
|
||||
"0",
|
||||
"0",
|
||||
),
|
||||
(
|
||||
"qwen3-coder-480b",
|
||||
"Qwen3 Coder 480B",
|
||||
"0.65",
|
||||
"3.25",
|
||||
"0",
|
||||
"0",
|
||||
),
|
||||
(
|
||||
"qwen3-coder-480b-a35b-instruct",
|
||||
"Qwen3 Coder 480B-A35B Instruct",
|
||||
"0.65",
|
||||
"3.25",
|
||||
"0",
|
||||
"0",
|
||||
),
|
||||
(
|
||||
"qwen3-coder-flash",
|
||||
"Qwen3 Coder Flash",
|
||||
@@ -1801,12 +1852,13 @@ impl Database {
|
||||
("grok-4", "Grok 4", "3", "15", "0.75", "0"),
|
||||
(
|
||||
"grok-code-fast-1",
|
||||
"Grok Code Fast",
|
||||
"Grok Build 0.1 (Code Fast Alias)",
|
||||
"1",
|
||||
"2",
|
||||
"0.20",
|
||||
"1.50",
|
||||
"0.02",
|
||||
"0",
|
||||
),
|
||||
("grok-build-0.1", "Grok Build 0.1", "1", "2", "0.20", "0"),
|
||||
("grok-3", "Grok 3", "3", "15", "0.75", "0"),
|
||||
("grok-3-mini", "Grok 3 Mini", "0.25", "0.50", "0.075", "0"),
|
||||
// Mistral 系列
|
||||
@@ -1898,6 +1950,96 @@ impl Database {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn repair_current_model_pricing(conn: &Connection) -> Result<(), AppError> {
|
||||
let pricing_fixes = [
|
||||
(
|
||||
"deepseek-v4-flash",
|
||||
"DeepSeek V4 Flash",
|
||||
"0.14",
|
||||
"0.28",
|
||||
"0.0028",
|
||||
"0",
|
||||
"0.14",
|
||||
"0.28",
|
||||
"0.028",
|
||||
"0",
|
||||
),
|
||||
(
|
||||
"deepseek-v4-pro",
|
||||
"DeepSeek V4 Pro",
|
||||
"0.435",
|
||||
"0.87",
|
||||
"0.003625",
|
||||
"0",
|
||||
"1.68",
|
||||
"3.36",
|
||||
"0.14",
|
||||
"0",
|
||||
),
|
||||
(
|
||||
"glm-5", "GLM-5", "1", "3.2", "0.2", "0", "0.72", "2.30", "0", "0",
|
||||
),
|
||||
(
|
||||
"glm-5.1", "GLM-5.1", "1.4", "4.4", "0.26", "0", "0.95", "3.15", "0", "0",
|
||||
),
|
||||
(
|
||||
"grok-code-fast-1",
|
||||
"Grok Build 0.1 (Code Fast Alias)",
|
||||
"1",
|
||||
"2",
|
||||
"0.20",
|
||||
"0",
|
||||
"0.20",
|
||||
"1.50",
|
||||
"0.02",
|
||||
"0",
|
||||
),
|
||||
];
|
||||
|
||||
for (
|
||||
model_id,
|
||||
display_name,
|
||||
input,
|
||||
output,
|
||||
cache_read,
|
||||
cache_creation,
|
||||
old_input,
|
||||
old_output,
|
||||
old_cache_read,
|
||||
old_cache_creation,
|
||||
) in pricing_fixes
|
||||
{
|
||||
conn.execute(
|
||||
"UPDATE model_pricing SET
|
||||
display_name = ?2,
|
||||
input_cost_per_million = ?3,
|
||||
output_cost_per_million = ?4,
|
||||
cache_read_cost_per_million = ?5,
|
||||
cache_creation_cost_per_million = ?6
|
||||
WHERE model_id = ?1
|
||||
AND input_cost_per_million = ?7
|
||||
AND output_cost_per_million = ?8
|
||||
AND cache_read_cost_per_million = ?9
|
||||
AND cache_creation_cost_per_million = ?10",
|
||||
rusqlite::params![
|
||||
model_id,
|
||||
display_name,
|
||||
input,
|
||||
output,
|
||||
cache_read,
|
||||
cache_creation,
|
||||
old_input,
|
||||
old_output,
|
||||
old_cache_read,
|
||||
old_cache_creation
|
||||
],
|
||||
)
|
||||
.map_err(|e| AppError::Database(format!("修复模型 {model_id} 定价失败: {e}")))?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// 确保模型定价表具备默认数据
|
||||
pub fn ensure_model_pricing_seeded(&self) -> Result<(), AppError> {
|
||||
let conn = lock_conn!(self.conn);
|
||||
@@ -1905,8 +2047,9 @@ impl Database {
|
||||
}
|
||||
|
||||
fn ensure_model_pricing_seeded_on_conn(conn: &Connection) -> Result<(), AppError> {
|
||||
// 每次启动都执行 INSERT OR IGNORE,增量追加新模型,已有数据不覆盖
|
||||
Self::seed_model_pricing(conn)
|
||||
// 每次启动都执行 INSERT OR IGNORE,增量追加新模型;仅修复仍等于旧内置值的定价。
|
||||
Self::seed_model_pricing(conn)?;
|
||||
Self::repair_current_model_pricing(conn)
|
||||
}
|
||||
|
||||
// --- 辅助方法 ---
|
||||
|
||||
@@ -662,6 +662,66 @@ fn schema_model_pricing_is_seeded_on_init() {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn model_pricing_seed_repairs_known_outdated_builtin_prices() {
|
||||
let db = Database::memory().expect("create memory db");
|
||||
|
||||
{
|
||||
let conn = db.conn.lock().expect("lock conn");
|
||||
conn.execute(
|
||||
"UPDATE model_pricing
|
||||
SET input_cost_per_million = '1.68',
|
||||
output_cost_per_million = '3.36',
|
||||
cache_read_cost_per_million = '0.14',
|
||||
cache_creation_cost_per_million = '0'
|
||||
WHERE model_id = 'deepseek-v4-pro'",
|
||||
[],
|
||||
)
|
||||
.expect("restore old DeepSeek price");
|
||||
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 = '0'
|
||||
WHERE model_id = 'glm-5.1'",
|
||||
[],
|
||||
)
|
||||
.expect("set custom GLM price");
|
||||
}
|
||||
|
||||
db.ensure_model_pricing_seeded()
|
||||
.expect("ensure pricing seeded");
|
||||
|
||||
let conn = db.conn.lock().expect("lock conn");
|
||||
let deepseek: (String, String, String) = conn
|
||||
.query_row(
|
||||
"SELECT input_cost_per_million, output_cost_per_million, cache_read_cost_per_million
|
||||
FROM model_pricing WHERE model_id = 'deepseek-v4-pro'",
|
||||
[],
|
||||
|row| Ok((row.get(0)?, row.get(1)?, row.get(2)?)),
|
||||
)
|
||||
.expect("query DeepSeek price");
|
||||
assert_eq!(
|
||||
deepseek,
|
||||
(
|
||||
"0.435".to_string(),
|
||||
"0.87".to_string(),
|
||||
"0.003625".to_string()
|
||||
)
|
||||
);
|
||||
|
||||
let glm: (String, String, String) = conn
|
||||
.query_row(
|
||||
"SELECT input_cost_per_million, output_cost_per_million, cache_read_cost_per_million
|
||||
FROM model_pricing WHERE model_id = 'glm-5.1'",
|
||||
[],
|
||||
|row| Ok((row.get(0)?, row.get(1)?, row.get(2)?)),
|
||||
)
|
||||
.expect("query GLM price");
|
||||
assert_eq!(glm, ("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");
|
||||
|
||||
@@ -380,7 +380,7 @@ mod tests {
|
||||
# Comment line
|
||||
GOOGLE_GEMINI_BASE_URL=https://example.com
|
||||
GEMINI_API_KEY=sk-test123
|
||||
GEMINI_MODEL=gemini-3-pro-preview
|
||||
GEMINI_MODEL=gemini-3.5-flash
|
||||
|
||||
# Another comment
|
||||
"#;
|
||||
@@ -395,7 +395,7 @@ GEMINI_MODEL=gemini-3-pro-preview
|
||||
assert_eq!(map.get("GEMINI_API_KEY"), Some(&"sk-test123".to_string()));
|
||||
assert_eq!(
|
||||
map.get("GEMINI_MODEL"),
|
||||
Some(&"gemini-3-pro-preview".to_string())
|
||||
Some(&"gemini-3.5-flash".to_string())
|
||||
);
|
||||
}
|
||||
|
||||
@@ -403,15 +403,12 @@ GEMINI_MODEL=gemini-3-pro-preview
|
||||
fn test_serialize_env_file() {
|
||||
let mut map = HashMap::new();
|
||||
map.insert("GEMINI_API_KEY".to_string(), "sk-test".to_string());
|
||||
map.insert(
|
||||
"GEMINI_MODEL".to_string(),
|
||||
"gemini-3-pro-preview".to_string(),
|
||||
);
|
||||
map.insert("GEMINI_MODEL".to_string(), "gemini-3.5-flash".to_string());
|
||||
|
||||
let content = serialize_env_file(&map);
|
||||
|
||||
assert!(content.contains("GEMINI_API_KEY=sk-test"));
|
||||
assert!(content.contains("GEMINI_MODEL=gemini-3-pro-preview"));
|
||||
assert!(content.contains("GEMINI_MODEL=gemini-3.5-flash"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -435,7 +432,7 @@ GEMINI_MODEL=gemini-3-pro-preview
|
||||
# Comment line
|
||||
GOOGLE_GEMINI_BASE_URL=https://example.com
|
||||
GEMINI_API_KEY=sk-test123
|
||||
GEMINI_MODEL=gemini-3-pro-preview
|
||||
GEMINI_MODEL=gemini-3.5-flash
|
||||
|
||||
# Another comment
|
||||
"#;
|
||||
@@ -452,7 +449,7 @@ GEMINI_MODEL=gemini-3-pro-preview
|
||||
assert_eq!(map.get("GEMINI_API_KEY"), Some(&"sk-test123".to_string()));
|
||||
assert_eq!(
|
||||
map.get("GEMINI_MODEL"),
|
||||
Some(&"gemini-3-pro-preview".to_string())
|
||||
Some(&"gemini-3.5-flash".to_string())
|
||||
);
|
||||
}
|
||||
|
||||
@@ -619,7 +616,7 @@ KEY_WITH-DASH=value";
|
||||
let settings = serde_json::json!({
|
||||
"env": {
|
||||
"GEMINI_API_KEY": "sk-test123",
|
||||
"GEMINI_MODEL": "gemini-3-pro-preview"
|
||||
"GEMINI_MODEL": "gemini-3.5-flash"
|
||||
}
|
||||
});
|
||||
|
||||
@@ -632,7 +629,7 @@ KEY_WITH-DASH=value";
|
||||
// 测试缺少 API Key 的非空配置在基本验证中可以通过(用户稍后填写)
|
||||
let settings = serde_json::json!({
|
||||
"env": {
|
||||
"GEMINI_MODEL": "gemini-3-pro-preview"
|
||||
"GEMINI_MODEL": "gemini-3.5-flash"
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -58,8 +58,8 @@ impl Default for StreamCheckConfig {
|
||||
max_retries: 2,
|
||||
degraded_threshold_ms: 6000,
|
||||
claude_model: "claude-haiku-4-5-20251001".to_string(),
|
||||
codex_model: "gpt-5.4@low".to_string(),
|
||||
gemini_model: "gemini-3-flash-preview".to_string(),
|
||||
codex_model: "gpt-5.5@low".to_string(),
|
||||
gemini_model: "gemini-3.5-flash".to_string(),
|
||||
test_prompt: default_test_prompt(),
|
||||
}
|
||||
}
|
||||
@@ -1886,7 +1886,7 @@ mod tests {
|
||||
AuthStrategy::Bearer,
|
||||
"openai_chat",
|
||||
true,
|
||||
"gpt-5.4",
|
||||
"gpt-5.5",
|
||||
);
|
||||
|
||||
assert_eq!(url, "https://relay.example/v1/chat/completions");
|
||||
@@ -1899,7 +1899,7 @@ mod tests {
|
||||
AuthStrategy::GitHubCopilot,
|
||||
"openai_chat",
|
||||
false,
|
||||
"gpt-5.4",
|
||||
"gpt-5.5",
|
||||
);
|
||||
|
||||
assert_eq!(url, "https://api.githubcopilot.com/chat/completions");
|
||||
@@ -1912,7 +1912,7 @@ mod tests {
|
||||
AuthStrategy::GitHubCopilot,
|
||||
"openai_responses",
|
||||
false,
|
||||
"gpt-5.4",
|
||||
"gpt-5.5",
|
||||
);
|
||||
|
||||
assert_eq!(url, "https://api.githubcopilot.com/v1/responses");
|
||||
@@ -1925,7 +1925,7 @@ mod tests {
|
||||
AuthStrategy::Bearer,
|
||||
"openai_chat",
|
||||
false,
|
||||
"gpt-5.4",
|
||||
"gpt-5.5",
|
||||
);
|
||||
|
||||
assert_eq!(url, "https://example.com/v1/chat/completions");
|
||||
@@ -1938,7 +1938,7 @@ mod tests {
|
||||
AuthStrategy::Bearer,
|
||||
"openai_responses",
|
||||
false,
|
||||
"gpt-5.4",
|
||||
"gpt-5.5",
|
||||
);
|
||||
|
||||
assert_eq!(url, "https://example.com/v1/responses");
|
||||
@@ -2005,7 +2005,7 @@ mod tests {
|
||||
#[test]
|
||||
fn test_resolve_claude_stream_url_for_gemini_native_cloudflare_vertex_full_url() {
|
||||
let url = StreamCheckService::resolve_claude_stream_url(
|
||||
"https://gateway.ai.cloudflare.com/v1/account/gateway/google-vertex-ai/v1/projects/project/locations/us-central1/publishers/google/models/gemini-3.1-pro-preview:streamGenerateContent",
|
||||
"https://gateway.ai.cloudflare.com/v1/account/gateway/google-vertex-ai/v1/projects/project/locations/us-central1/publishers/google/models/gemini-3.5-flash:streamGenerateContent",
|
||||
AuthStrategy::Google,
|
||||
"gemini_native",
|
||||
true,
|
||||
@@ -2014,7 +2014,7 @@ mod tests {
|
||||
|
||||
assert_eq!(
|
||||
url,
|
||||
"https://gateway.ai.cloudflare.com/v1/account/gateway/google-vertex-ai/v1/projects/project/locations/us-central1/publishers/google/models/gemini-3.1-pro-preview:streamGenerateContent?alt=sse"
|
||||
"https://gateway.ai.cloudflare.com/v1/account/gateway/google-vertex-ai/v1/projects/project/locations/us-central1/publishers/google/models/gemini-3.5-flash:streamGenerateContent?alt=sse"
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user