fix(ai): DeepSeek 余额按官方 balance_infos 解析
/user/balance 返回的是 {is_available, balance_infos[]},没有顶层 data,
旧实现永远解析出 None,连接测试会显示「余额 None CNY」。同时补全
TotHost 预设平台,新环境初始化后即可直接配 whmcs-panel。
This commit is contained in:
+11
-3
@@ -34,12 +34,20 @@ class DeepSeekAdapter(_AIBase):
|
|||||||
BASE = "https://api.deepseek.com"
|
BASE = "https://api.deepseek.com"
|
||||||
|
|
||||||
def _balance(self) -> AccountInfo:
|
def _balance(self) -> AccountInfo:
|
||||||
data = self._get(self.BASE + "/user/balance").get("data", {})
|
# 官方响应为 {is_available, balance_infos: [{currency,total_balance,...}]},
|
||||||
|
# 没有顶层 data;同时兼容旧版 data.total_balance 形态。
|
||||||
|
data = self._get(self.BASE + "/user/balance")
|
||||||
|
infos = data.get("balance_infos") or []
|
||||||
|
info = next((i for i in infos if i.get("currency") == "CNY"), infos[0] if infos else {})
|
||||||
|
if not info and isinstance(data.get("data"), dict):
|
||||||
|
info = data["data"]
|
||||||
try:
|
try:
|
||||||
total = float(data.get("total_balance")) if data.get("total_balance") is not None else None
|
total = float(info.get("total_balance")) if info.get("total_balance") is not None else None
|
||||||
except (TypeError, ValueError):
|
except (TypeError, ValueError):
|
||||||
total = None
|
total = None
|
||||||
return AccountInfo(balance=total, currency="CNY", raw=data)
|
return AccountInfo(
|
||||||
|
balance=total, currency=info.get("currency") or "CNY", raw=data
|
||||||
|
)
|
||||||
|
|
||||||
def test_connection(self) -> dict:
|
def test_connection(self) -> dict:
|
||||||
try:
|
try:
|
||||||
|
|||||||
@@ -35,6 +35,10 @@ PRESET_PROVIDERS = [
|
|||||||
{"slug": "linode", "name": "Linode", "name_en": "Akamai Linode", "category": "vps",
|
{"slug": "linode", "name": "Linode", "name_en": "Akamai Linode", "category": "vps",
|
||||||
"services": "vps,domain,dns",
|
"services": "vps,domain,dns",
|
||||||
"website": "https://linode.com", "console_url": "https://cloud.linode.com", "sdk_type": "linode-api"},
|
"website": "https://linode.com", "console_url": "https://cloud.linode.com", "sdk_type": "linode-api"},
|
||||||
|
# WHMCS 面板类平台没有开放 API,通过账号关联的登录凭据进入客户区只读核对。
|
||||||
|
{"slug": "tothost", "name": "TotHost", "name_en": "TotHost", "category": "vps",
|
||||||
|
"services": "vps",
|
||||||
|
"website": "https://www.tothost.vn", "console_url": "https://billing.tothost.vn/clientarea.php", "sdk_type": "whmcs-panel"},
|
||||||
# 以下平台暂无适配器:sdk_type 必须留空。
|
# 以下平台暂无适配器:sdk_type 必须留空。
|
||||||
# 填了不存在的 sdk_type 会让平台卡片显示「API 对接: xxx」,用户建账号点同步才撞
|
# 填了不存在的 sdk_type 会让平台卡片显示「API 对接: xxx」,用户建账号点同步才撞
|
||||||
# 「暂不支持的 SDK 类型」——声明支持却不支持,比明说不支持更糟。
|
# 「暂不支持的 SDK 类型」——声明支持却不支持,比明说不支持更糟。
|
||||||
|
|||||||
+32
-2
@@ -212,17 +212,47 @@ def test_registry_get_adapter_routing():
|
|||||||
|
|
||||||
# --------------------------- AI 平台适配器 --------------------------- #
|
# --------------------------- AI 平台适配器 --------------------------- #
|
||||||
def test_deepseek_balance_normalization():
|
def test_deepseek_balance_normalization():
|
||||||
|
"""官方 /user/balance 返回 balance_infos,不是顶层 data"""
|
||||||
adapter = DeepSeekAdapter({"api_key": "x"})
|
adapter = DeepSeekAdapter({"api_key": "x"})
|
||||||
mock = {"data": {"total_balance": "88.50", "granted_balance": "10.00"}}
|
mock = {
|
||||||
|
"is_available": True,
|
||||||
|
"balance_infos": [{
|
||||||
|
"currency": "CNY",
|
||||||
|
"total_balance": "88.50",
|
||||||
|
"granted_balance": "10.00",
|
||||||
|
"topped_up_balance": "78.50",
|
||||||
|
}],
|
||||||
|
}
|
||||||
with patch.object(DeepSeekAdapter, "_get", return_value=mock):
|
with patch.object(DeepSeekAdapter, "_get", return_value=mock):
|
||||||
acc = adapter.get_account()
|
acc = adapter.get_account()
|
||||||
assert acc.balance == 88.5 # 字符串 -> float
|
assert acc.balance == 88.5 # 字符串 -> float
|
||||||
assert acc.currency == "CNY"
|
assert acc.currency == "CNY"
|
||||||
|
|
||||||
|
|
||||||
|
def test_deepseek_balance_prefers_cny_when_multiple():
|
||||||
|
adapter = DeepSeekAdapter({"api_key": "x"})
|
||||||
|
mock = {"balance_infos": [
|
||||||
|
{"currency": "USD", "total_balance": "1.00"},
|
||||||
|
{"currency": "CNY", "total_balance": "7.20"},
|
||||||
|
]}
|
||||||
|
with patch.object(DeepSeekAdapter, "_get", return_value=mock):
|
||||||
|
acc = adapter.get_account()
|
||||||
|
assert acc.balance == 7.2
|
||||||
|
assert acc.currency == "CNY"
|
||||||
|
|
||||||
|
|
||||||
|
def test_deepseek_balance_supports_legacy_shape():
|
||||||
|
"""兼容旧响应 data.total_balance,避免上线后旧 mock/代理失效"""
|
||||||
|
adapter = DeepSeekAdapter({"api_key": "x"})
|
||||||
|
with patch.object(DeepSeekAdapter, "_get", return_value={"data": {"total_balance": "5.00"}}):
|
||||||
|
acc = adapter.get_account()
|
||||||
|
assert acc.balance == 5.0
|
||||||
|
|
||||||
|
|
||||||
def test_deepseek_test_connection_includes_balance():
|
def test_deepseek_test_connection_includes_balance():
|
||||||
adapter = DeepSeekAdapter({"api_key": "x"})
|
adapter = DeepSeekAdapter({"api_key": "x"})
|
||||||
with patch.object(DeepSeekAdapter, "_get", return_value={"data": {"total_balance": "88.50"}}):
|
mock = {"balance_infos": [{"currency": "CNY", "total_balance": "88.50"}]}
|
||||||
|
with patch.object(DeepSeekAdapter, "_get", return_value=mock):
|
||||||
result = adapter.test_connection()
|
result = adapter.test_connection()
|
||||||
assert result["ok"] is True
|
assert result["ok"] is True
|
||||||
assert "88.5" in result["message"]
|
assert "88.5" in result["message"]
|
||||||
|
|||||||
Reference in New Issue
Block a user