mirror of
https://github.com/farion1231/cc-switch.git
synced 2026-08-02 18:41:35 +08:00
fix(presets): use dated Doubao model id and price 6-digit dated models
Volcengine Ark rejects the bare model name doubao-seed-2-1-pro with a 404
("model does not exist or you do not have access to it") even after the
model is activated; the API requires the full dated id
doubao-seed-2-1-pro-260628. Update the Doubao preset model id across all
apps (config default, generated catalog, and OpenClaw namespaced refs).
Pricing lookup only stripped 8-digit (YYYYMMDD) and ISO date suffixes, not
the 6-digit YYMMDD format Volcengine uses (-260628, -250615), so real
Doubao usage never matched the bare-name pricing seed and showed $0 cost.
Extend strip_model_date_suffix to also strip 6-digit YYMMDD (with
month/day validation to avoid eating non-date version suffixes), keeping
the bare-name pricing seed as the canonical identity. This also fixes
pricing for every other Volcengine Doubao model. Add unit and end-to-end
regression tests.
This commit is contained in:
@@ -2655,7 +2655,7 @@ web_search = "disabled"
|
|||||||
("gpt-5.5", "https://www.packyapi.com/v1"),
|
("gpt-5.5", "https://www.packyapi.com/v1"),
|
||||||
("gpt-5-codex", "https://aihubmix.com/v1"),
|
("gpt-5-codex", "https://aihubmix.com/v1"),
|
||||||
(
|
(
|
||||||
"doubao-seed-2-1-pro",
|
"doubao-seed-2-1-pro-260628",
|
||||||
"https://ark.cn-beijing.volces.com/api/v3",
|
"https://ark.cn-beijing.volces.com/api/v3",
|
||||||
),
|
),
|
||||||
("Pro/moonshotai/Kimi-K2.6", "https://api.siliconflow.cn/v1"),
|
("Pro/moonshotai/Kimi-K2.6", "https://api.siliconflow.cn/v1"),
|
||||||
|
|||||||
@@ -2197,8 +2197,24 @@ fn strip_model_date_suffix(model_id: &str) -> Option<String> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let (base, suffix) = model_id.rsplit_once('-')?;
|
let (base, suffix) = model_id.rsplit_once('-')?;
|
||||||
(!base.is_empty() && suffix.len() == 8 && suffix.chars().all(|c| c.is_ascii_digit()))
|
if base.is_empty() || !suffix.chars().all(|c| c.is_ascii_digit()) {
|
||||||
.then(|| base.to_string())
|
return None;
|
||||||
|
}
|
||||||
|
// 8 位 YYYYMMDD(如 -20250615;OpenAI / Claude / 通义千问等)。
|
||||||
|
if suffix.len() == 8 {
|
||||||
|
return Some(base.to_string());
|
||||||
|
}
|
||||||
|
// 6 位 YYMMDD(如 -260628;火山方舟 doubao-seed-*、部分国产厂商)。
|
||||||
|
// 6 位比 8 位更易误伤非日期尾巴(如 -123456 的版本号),故额外校验
|
||||||
|
// 月 01-12、日 01-31 才剥离;剥不动时退回 None 由上层精确匹配兜底。
|
||||||
|
if suffix.len() == 6 {
|
||||||
|
let month: u32 = suffix[2..4].parse().unwrap_or(0);
|
||||||
|
let day: u32 = suffix[4..6].parse().unwrap_or(0);
|
||||||
|
if (1..=12).contains(&month) && (1..=31).contains(&day) {
|
||||||
|
return Some(base.to_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
fn strip_reasoning_effort_suffix(model_id: &str) -> Option<String> {
|
fn strip_reasoning_effort_suffix(model_id: &str) -> Option<String> {
|
||||||
@@ -3859,6 +3875,55 @@ mod tests {
|
|||||||
assert_eq!(strip_model_date_suffix("abc🚀12345678"), None);
|
assert_eq!(strip_model_date_suffix("abc🚀12345678"), None);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_strip_model_date_suffix_handles_six_digit_yymmdd() {
|
||||||
|
// 火山方舟 6 位 YYMMDD 后缀应被剥离(doubao 全系都用这种格式)。
|
||||||
|
assert_eq!(
|
||||||
|
strip_model_date_suffix("doubao-seed-2-1-pro-260628").as_deref(),
|
||||||
|
Some("doubao-seed-2-1-pro")
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
strip_model_date_suffix("doubao-seed-1-6-250615").as_deref(),
|
||||||
|
Some("doubao-seed-1-6")
|
||||||
|
);
|
||||||
|
// 8 位 YYYYMMDD 仍照旧剥离。
|
||||||
|
assert_eq!(
|
||||||
|
strip_model_date_suffix("claude-3-5-sonnet-20241022").as_deref(),
|
||||||
|
Some("claude-3-5-sonnet")
|
||||||
|
);
|
||||||
|
// 月/日非法的 6 位尾巴(版本号等)不剥离,避免误伤。
|
||||||
|
assert_eq!(strip_model_date_suffix("foo-bar-123456"), None); // 月=34
|
||||||
|
assert_eq!(strip_model_date_suffix("widget-209900"), None); // 月=99
|
||||||
|
assert_eq!(strip_model_date_suffix("gizmo-251200"), None); // 日=00
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_pricing_resolves_volcengine_dated_model_to_bare_seed_row() -> Result<(), AppError> {
|
||||||
|
// 回归:火山真实用量带 6 位日期后缀(doubao-seed-2-1-pro-260628),
|
||||||
|
// 必须能归一化命中定价表里的裸名 seed 行(doubao-seed-2-1-pro),否则成本显示 $0。
|
||||||
|
let db = Database::memory()?;
|
||||||
|
let conn = lock_conn!(db.conn);
|
||||||
|
|
||||||
|
conn.execute(
|
||||||
|
"INSERT OR REPLACE INTO model_pricing (
|
||||||
|
model_id, display_name, input_cost_per_million, output_cost_per_million,
|
||||||
|
cache_read_cost_per_million, cache_creation_cost_per_million
|
||||||
|
) VALUES ('doubao-seed-2-1-pro', 'Doubao Seed 2.1 Pro', '0.84', '4.2', '0.17', '0')",
|
||||||
|
[],
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let row = find_model_pricing_row(&conn, "doubao-seed-2-1-pro-260628")?;
|
||||||
|
assert!(
|
||||||
|
row.is_some(),
|
||||||
|
"带日期的火山模型应通过 6 位日期剥离命中裸名定价行"
|
||||||
|
);
|
||||||
|
let (input, output, ..) = row.unwrap();
|
||||||
|
assert_eq!(input, "0.84");
|
||||||
|
assert_eq!(output, "4.2");
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_prefix_pricing_does_not_match_short_base_model_to_variant() -> Result<(), AppError> {
|
fn test_prefix_pricing_does_not_match_short_base_model_to_variant() -> Result<(), AppError> {
|
||||||
let db = Database::memory()?;
|
let db = Database::memory()?;
|
||||||
|
|||||||
@@ -232,9 +232,9 @@ export const claudeDesktopProviderPresets: ClaudeDesktopProviderPreset[] = [
|
|||||||
mode: "proxy",
|
mode: "proxy",
|
||||||
apiFormat: "anthropic",
|
apiFormat: "anthropic",
|
||||||
modelRoutes: brandedRoutes(
|
modelRoutes: brandedRoutes(
|
||||||
"doubao-seed-2-1-pro",
|
"doubao-seed-2-1-pro-260628",
|
||||||
"doubao-seed-2-1-pro",
|
"doubao-seed-2-1-pro-260628",
|
||||||
"doubao-seed-2-1-pro",
|
"doubao-seed-2-1-pro-260628",
|
||||||
),
|
),
|
||||||
isPartner: true,
|
isPartner: true,
|
||||||
partnerPromotionKey: "doubaoseed",
|
partnerPromotionKey: "doubaoseed",
|
||||||
|
|||||||
@@ -179,10 +179,10 @@ export const providerPresets: ProviderPreset[] = [
|
|||||||
ANTHROPIC_BASE_URL: "https://ark.cn-beijing.volces.com/api/compatible",
|
ANTHROPIC_BASE_URL: "https://ark.cn-beijing.volces.com/api/compatible",
|
||||||
ANTHROPIC_AUTH_TOKEN: "",
|
ANTHROPIC_AUTH_TOKEN: "",
|
||||||
API_TIMEOUT_MS: "3000000",
|
API_TIMEOUT_MS: "3000000",
|
||||||
ANTHROPIC_MODEL: "doubao-seed-2-1-pro",
|
ANTHROPIC_MODEL: "doubao-seed-2-1-pro-260628",
|
||||||
ANTHROPIC_DEFAULT_SONNET_MODEL: "doubao-seed-2-1-pro",
|
ANTHROPIC_DEFAULT_SONNET_MODEL: "doubao-seed-2-1-pro-260628",
|
||||||
ANTHROPIC_DEFAULT_OPUS_MODEL: "doubao-seed-2-1-pro",
|
ANTHROPIC_DEFAULT_OPUS_MODEL: "doubao-seed-2-1-pro-260628",
|
||||||
ANTHROPIC_DEFAULT_HAIKU_MODEL: "doubao-seed-2-1-pro",
|
ANTHROPIC_DEFAULT_HAIKU_MODEL: "doubao-seed-2-1-pro-260628",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
category: "cn_official",
|
category: "cn_official",
|
||||||
|
|||||||
@@ -212,7 +212,7 @@ export const codexProviderPresets: CodexProviderPreset[] = [
|
|||||||
config: generateThirdPartyConfig(
|
config: generateThirdPartyConfig(
|
||||||
"doubaoseed",
|
"doubaoseed",
|
||||||
"https://ark.cn-beijing.volces.com/api/v3",
|
"https://ark.cn-beijing.volces.com/api/v3",
|
||||||
"doubao-seed-2-1-pro",
|
"doubao-seed-2-1-pro-260628",
|
||||||
),
|
),
|
||||||
endpointCandidates: ["https://ark.cn-beijing.volces.com/api/v3"],
|
endpointCandidates: ["https://ark.cn-beijing.volces.com/api/v3"],
|
||||||
// 火山方舟主数据面 /api/v3 原生支持 Responses API(/api/v3/responses),无需路由接管转换
|
// 火山方舟主数据面 /api/v3 原生支持 Responses API(/api/v3/responses),无需路由接管转换
|
||||||
@@ -221,7 +221,7 @@ export const codexProviderPresets: CodexProviderPreset[] = [
|
|||||||
// 让 Codex 直连显示模型并避免 custom 工具被网关拒绝
|
// 让 Codex 直连显示模型并避免 custom 工具被网关拒绝
|
||||||
modelCatalog: modelCatalog([
|
modelCatalog: modelCatalog([
|
||||||
{
|
{
|
||||||
model: "doubao-seed-2-1-pro",
|
model: "doubao-seed-2-1-pro-260628",
|
||||||
displayName: "Doubao Seed 2.1 Pro",
|
displayName: "Doubao Seed 2.1 Pro",
|
||||||
contextWindow: 262144,
|
contextWindow: 262144,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -219,7 +219,7 @@ export const hermesProviderPresets: HermesProviderPreset[] = [
|
|||||||
api_mode: "anthropic_messages",
|
api_mode: "anthropic_messages",
|
||||||
models: [
|
models: [
|
||||||
{
|
{
|
||||||
id: "doubao-seed-2-1-pro",
|
id: "doubao-seed-2-1-pro-260628",
|
||||||
name: "Doubao Seed 2.1 Pro",
|
name: "Doubao Seed 2.1 Pro",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@@ -231,7 +231,7 @@ export const hermesProviderPresets: HermesProviderPreset[] = [
|
|||||||
iconColor: "#3370FF",
|
iconColor: "#3370FF",
|
||||||
suggestedDefaults: {
|
suggestedDefaults: {
|
||||||
model: {
|
model: {
|
||||||
default: "doubao-seed-2-1-pro",
|
default: "doubao-seed-2-1-pro-260628",
|
||||||
provider: "doubao_seed",
|
provider: "doubao_seed",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -228,7 +228,7 @@ export const openclawProviderPresets: OpenClawProviderPreset[] = [
|
|||||||
api: "openai-completions",
|
api: "openai-completions",
|
||||||
models: [
|
models: [
|
||||||
{
|
{
|
||||||
id: "doubao-seed-2-1-pro",
|
id: "doubao-seed-2-1-pro-260628",
|
||||||
name: "DouBao Seed 2.1 Pro",
|
name: "DouBao Seed 2.1 Pro",
|
||||||
contextWindow: 128000,
|
contextWindow: 128000,
|
||||||
cost: { input: 0.84, output: 4.2 },
|
cost: { input: 0.84, output: 4.2 },
|
||||||
@@ -248,9 +248,9 @@ export const openclawProviderPresets: OpenClawProviderPreset[] = [
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
suggestedDefaults: {
|
suggestedDefaults: {
|
||||||
model: { primary: "doubaoseed/doubao-seed-2-1-pro" },
|
model: { primary: "doubaoseed/doubao-seed-2-1-pro-260628" },
|
||||||
modelCatalog: {
|
modelCatalog: {
|
||||||
"doubaoseed/doubao-seed-2-1-pro": { alias: "DouBao" },
|
"doubaoseed/doubao-seed-2-1-pro-260628": { alias: "DouBao" },
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -387,7 +387,7 @@ export const opencodeProviderPresets: OpenCodeProviderPreset[] = [
|
|||||||
setCacheKey: true,
|
setCacheKey: true,
|
||||||
},
|
},
|
||||||
models: {
|
models: {
|
||||||
"doubao-seed-2-1-pro": {
|
"doubao-seed-2-1-pro-260628": {
|
||||||
name: "Doubao Seed 2.1 Pro",
|
name: "Doubao Seed 2.1 Pro",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -158,7 +158,10 @@ describe("Codex Chat provider presets", () => {
|
|||||||
string,
|
string,
|
||||||
{ contextWindows: Record<string, number> }
|
{ contextWindows: Record<string, number> }
|
||||||
>([
|
>([
|
||||||
["DouBaoSeed", { contextWindows: { "doubao-seed-2-1-pro": 262144 } }],
|
[
|
||||||
|
"DouBaoSeed",
|
||||||
|
{ contextWindows: { "doubao-seed-2-1-pro-260628": 262144 } },
|
||||||
|
],
|
||||||
["Bailian", { contextWindows: { "qwen3-coder-plus": 1048576 } }],
|
["Bailian", { contextWindows: { "qwen3-coder-plus": 1048576 } }],
|
||||||
["Longcat", { contextWindows: { "LongCat-2.0-Preview": 1048576 } }],
|
["Longcat", { contextWindows: { "LongCat-2.0-Preview": 1048576 } }],
|
||||||
["MiniMax", { contextWindows: { "MiniMax-M3": 1000000 } }],
|
["MiniMax", { contextWindows: { "MiniMax-M3": 1000000 } }],
|
||||||
|
|||||||
Reference in New Issue
Block a user