fix(usage): stop treating an empty DeepSeek balance as an expired plan

DeepSeek's /user/balance returns `is_available`, which only says whether
the balance is enough for API calls. It turns false as soon as the
balance reaches zero or goes negative, while the key itself still works.
The parser mapped it straight to `is_valid`, so the provider card and the
tray showed "plan expired" and dropped the balance entirely.

Always report the balance as valid and leave invalid keys to the
existing 401/403 auth-error path. A zero or negative balance now goes
through the normal balance line, which shows the amount in red on the
card and amber in the tray, the same as OpenRouter and Novita.

Refs #8812
This commit is contained in:
Jason committed 2026-10-24 22:56:15 +08:00
1 parent f395cd6be5
commit b9f317af51
1 file changed
+19 -10
+19 -10
View File
@@ -111,11 +111,9 @@ async fn query_deepseek(api_key: &str) -> Result<UsageResult, String> {
Ok(parse_deepseek_balance(&body))
}
// `is_available` 只表示余额够不够调用,不代表 Key 失效(失效是 401/403,上面已处理),
// 所以不拿它判 is_valid,否则零余额 / 欠费会被显示成「套餐已过期」;交给 UI 按余额红字显示(#8812)。
fn parse_deepseek_balance(body: &serde_json::Value) -> UsageResult {
let is_available = body
.get("is_available")
.and_then(|v| v.as_bool())
.unwrap_or(true);
let mut data = Vec::new();
if let Some(infos) = body.get("balance_infos").and_then(|v| v.as_array()) {
@@ -132,12 +130,8 @@ fn parse_deepseek_balance(body: &serde_json::Value) -> UsageResult {
total: None,
used: None,
unit: Some(currency.to_string()),
is_valid: Some(is_available),
invalid_message: if !is_available {
Some("Insufficient balance".to_string())
} else {
None
},
is_valid: Some(true),
invalid_message: None,
extra: None,
});
}
@@ -679,6 +673,21 @@ mod tests {
assert_eq!(result.data.unwrap()[0].remaining, Some(0.0));
}
#[test]
fn deepseek_unavailable_balance_does_not_determine_key_validity() {
for (raw, expected) in [("-24.96", -24.96), ("-0.00", 0.0)] {
let result = parse_deepseek_balance(&json!({
"is_available": false,
"balance_infos": [{ "currency": "CNY", "total_balance": raw }]
}));
assert!(result.success);
let balance = result.data.unwrap().remove(0);
assert_eq!(balance.remaining, Some(expected));
assert_eq!(balance.is_valid, Some(true));
assert!(balance.invalid_message.is_none());
}
}
#[test]
fn novita_balance_does_not_determine_key_validity() {
for (raw, available) in [(0.0, 0.0), (-50000.0, -5.0), (255000.0, 25.5)] {