feat: AI平台适配器(DeepSeek/Kimi/Open余额查询+Minimax骨架)+ capabilities按钮控制,测试17项通过
This commit is contained in:
@@ -0,0 +1,110 @@
|
||||
"""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 适配器余额接口待完善(请提供具体接口文档)"}
|
||||
Reference in New Issue
Block a user