Files
vps-manager/app/adapters/ai.py
T

111 lines
4.1 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""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 适配器余额接口待完善(请提供具体接口文档)"}