"""AI 平台适配器(验证 Key + 余额/用量查询) 各 AI 平台 API 差异较大,此处实现 test_connection(验证 Key,附带余额) 与 get_account(余额)。余额接口可能随平台调整,填入真实凭证后以实际返回为准。 所需配置统一为 {"api_key": "..."}(Minimax 另需 group_id)。 """ import httpx from app.adapters.base import AccountInfo, BaseAdapter from app.adapters.registry import register class _AIBase(BaseAdapter): """AI 适配器公共逻辑(Bearer Key + JSON GET)""" required_config = ["api_key"] def _headers(self) -> dict: return {"Authorization": f"Bearer {self.config.get('api_key', '')}"} def _get(self, url: str) -> dict: with httpx.Client(timeout=30) as client: resp = client.get(url, headers=self._headers()) resp.raise_for_status() return resp.json() def _http_error(self, e: httpx.HTTPStatusError) -> dict: return {"ok": False, "message": f"HTTP {e.response.status_code}:API Key 无效或权限不足"} @register("deepseek-api") class DeepSeekAdapter(_AIBase): BASE = "https://api.deepseek.com" def _balance(self) -> AccountInfo: data = self._get(self.BASE + "/user/balance").get("data", {}) try: total = float(data.get("total_balance")) if data.get("total_balance") is not None else None except (TypeError, ValueError): total = None return AccountInfo(balance=total, currency="CNY", raw=data) def test_connection(self) -> dict: try: acc = self._balance() return {"ok": True, "message": f"连接成功,余额 {acc.balance} CNY"} except httpx.HTTPStatusError as e: return self._http_error(e) except Exception as e: # noqa: BLE001 return {"ok": False, "message": str(e)} def get_account(self) -> AccountInfo: return self._balance() @register("moonshot-api") class KimiAdapter(_AIBase): BASE = "https://api.moonshot.cn/v1" def _balance(self) -> AccountInfo: data = self._get(self.BASE + "/users/me/balance").get("data", {}) return AccountInfo(balance=data.get("available_balance"), currency="CNY", raw=data) def test_connection(self) -> dict: try: acc = self._balance() return {"ok": True, "message": f"连接成功,可用余额 {acc.balance} CNY"} except httpx.HTTPStatusError as e: return self._http_error(e) except Exception as e: # noqa: BLE001 return {"ok": False, "message": str(e)} def get_account(self) -> AccountInfo: return self._balance() @register("openai-api") class OpenAIAdapter(_AIBase): BASE = "https://api.openai.com/v1" def test_connection(self) -> dict: try: data = self._get(self.BASE + "/models") count = len(data.get("data", [])) return {"ok": True, "message": f"连接成功,可用模型 {count} 个"} except httpx.HTTPStatusError as e: return self._http_error(e) except Exception as e: # noqa: BLE001 return {"ok": False, "message": str(e)} def get_account(self) -> AccountInfo: # OpenAI 余额接口需组织级权限,尝试旧版 billing,失败返回空 try: data = self._get("https://api.openai.com/dashboard/billing/credit_grants") return AccountInfo(balance=data.get("total_available"), currency="USD", raw=data) except Exception: # noqa: BLE001 return AccountInfo(balance=None, currency="USD", raw={}) @register("minimax-api") class MinimaxAdapter(_AIBase): # Minimax 需 group_id,余额接口因账号类型而异,此处为骨架待完善 required_config = ["api_key", "group_id"] BASE = "https://api.minimax.chat/v1" def test_connection(self) -> dict: if not self.config.get("group_id"): return {"ok": False, "message": "Minimax 需配置 group_id(适配器余额接口待完善)"} return {"ok": False, "message": "Minimax 适配器余额接口待完善(请提供具体接口文档)"}