chore: 升级到 Python 3.14 并现代化代码

- 升级 Docker 基础镜像从 Python 3.12 到 3.14
- 更新 pyproject.toml 支持 Python 3.13/3.14
- 移除 Python 3.8/3.9/3.10/3.11 分类器
- 更新 black 和 mypy 配置目标版本
- 将 get_event_loop() 替换为 get_running_loop() 加上 RuntimeError 处理
- 简化 compute_cost_sync 中的 asyncio.run 使用
- Dict/List/Tuple/Set → dict/list/tuple/set (PEP 585)
- Optional[T] → T | None (PEP 604)
- Union[A, B] → A | B (PEP 604)
- 移除废弃的 typing 导入
- 移除不必要的字符串引号注解
This commit is contained in:
AAEE86
2026-01-30 03:10:21 +08:00
parent 3e75bc8964
commit 24d24f6829
255 changed files with 4062 additions and 4173 deletions

View File

@@ -3,7 +3,7 @@ Anyrouter 余额查询操作(含自动签到)
"""
import time
from typing import Any, Dict, Optional
from typing import Any
import httpx
@@ -101,7 +101,7 @@ class AnyrouterBalanceAction(BalanceAction):
)
def _handle_http_error(
self, response: httpx.Response, raw_data: Optional[Dict[str, Any]] = None
self, response: httpx.Response, raw_data: dict[str, Any] | None = None
) -> ActionResult:
"""处理 HTTP 错误响应"""
status_code = response.status_code
@@ -117,7 +117,7 @@ class AnyrouterBalanceAction(BalanceAction):
return super()._handle_http_error(response, raw_data)
async def _do_checkin(self, client: httpx.AsyncClient) -> Optional[Dict[str, Any]]:
async def _do_checkin(self, client: httpx.AsyncClient) -> dict[str, Any] | None:
"""
执行自动签到(始终执行)

View File

@@ -3,7 +3,7 @@
"""
from abc import abstractmethod
from typing import Any, Dict, Optional
from typing import Any
import httpx
@@ -80,7 +80,7 @@ class BalanceAction(ProviderAction):
"""
pass
async def _do_checkin(self, client: httpx.AsyncClient) -> Optional[Dict[str, Any]]:
async def _do_checkin(self, client: httpx.AsyncClient) -> dict[str, Any] | None:
"""
执行签到(子类可选实现)
@@ -97,11 +97,11 @@ class BalanceAction(ProviderAction):
def _create_balance_info(
self,
total_granted: Optional[float] = None,
total_used: Optional[float] = None,
total_available: Optional[float] = None,
total_granted: float | None = None,
total_used: float | None = None,
total_available: float | None = None,
currency: str = "USD",
extra: Optional[Dict[str, Any]] = None,
extra: dict[str, Any] | None = None,
) -> BalanceInfo:
"""
创建余额信息对象
@@ -135,7 +135,7 @@ class BalanceAction(ProviderAction):
extra=extra if extra is not None else {},
)
def _to_float(self, value: Any) -> Optional[float]:
def _to_float(self, value: Any) -> float | None:
"""转换为浮点数"""
if value is None:
return None
@@ -145,7 +145,7 @@ class BalanceAction(ProviderAction):
return None
@classmethod
def get_config_schema(cls) -> Dict[str, Any]:
def get_config_schema(cls) -> dict[str, Any]:
"""获取操作配置 schema子类可重写"""
return {
"type": "object",

View File

@@ -3,8 +3,7 @@ Provider 操作抽象基类
"""
from abc import ABC, abstractmethod
from datetime import datetime, timezone
from typing import Any, Dict, Optional
from typing import Any
import httpx
@@ -30,7 +29,7 @@ class ProviderAction(ABC):
# 默认缓存时间(秒)
default_cache_ttl: int = 300
def __init__(self, config: Optional[Dict[str, Any]] = None):
def __init__(self, config: dict[str, Any] | None = None):
"""
初始化操作
@@ -52,7 +51,7 @@ class ProviderAction(ABC):
"""
pass
def _extract_field(self, data: Any, path: Optional[str]) -> Any:
def _extract_field(self, data: Any, path: str | None) -> Any:
"""
从响应数据中提取字段
@@ -86,9 +85,9 @@ class ProviderAction(ABC):
def _make_success_result(
self,
data: Any = None,
message: Optional[str] = None,
response_time_ms: Optional[int] = None,
raw_response: Optional[Dict[str, Any]] = None,
message: str | None = None,
response_time_ms: int | None = None,
raw_response: dict[str, Any] | None = None,
) -> ActionResult:
"""创建成功结果"""
return ActionResult(
@@ -104,9 +103,9 @@ class ProviderAction(ABC):
def _make_error_result(
self,
status: ActionStatus,
message: Optional[str] = None,
retry_after_seconds: Optional[int] = None,
raw_response: Optional[Dict[str, Any]] = None,
message: str | None = None,
retry_after_seconds: int | None = None,
raw_response: dict[str, Any] | None = None,
) -> ActionResult:
"""创建错误结果"""
return ActionResult(
@@ -119,7 +118,7 @@ class ProviderAction(ABC):
)
def _handle_http_error(
self, response: httpx.Response, raw_data: Optional[Dict[str, Any]] = None
self, response: httpx.Response, raw_data: dict[str, Any] | None = None
) -> ActionResult:
"""处理 HTTP 错误响应"""
status_code = response.status_code
@@ -153,7 +152,7 @@ class ProviderAction(ABC):
)
@classmethod
def get_config_schema(cls) -> Dict[str, Any]:
def get_config_schema(cls) -> dict[str, Any]:
"""
获取操作配置 JSON Schema用于前端表单生成

View File

@@ -3,7 +3,7 @@
"""
from abc import abstractmethod
from typing import Any, Dict, Optional
from typing import Any
import httpx
@@ -44,10 +44,10 @@ class CheckinAction(ProviderAction):
def _create_checkin_info(
self,
reward: Optional[float] = None,
streak_days: Optional[int] = None,
message: Optional[str] = None,
extra: Optional[Dict[str, Any]] = None,
reward: float | None = None,
streak_days: int | None = None,
message: str | None = None,
extra: dict[str, Any] | None = None,
) -> CheckinInfo:
"""
创建签到信息对象
@@ -71,7 +71,7 @@ class CheckinAction(ProviderAction):
)
@classmethod
def get_config_schema(cls) -> Dict[str, Any]:
def get_config_schema(cls) -> dict[str, Any]:
"""获取操作配置 schema子类可重写"""
return {
"type": "object",

View File

@@ -3,7 +3,7 @@ Cubence 余额查询操作
"""
import time
from typing import Any, Dict, Optional
from typing import Any
import httpx
@@ -85,7 +85,7 @@ class CubenceBalanceAction(BalanceAction):
)
def _handle_http_error(
self, response: httpx.Response, raw_data: Optional[Dict[str, Any]] = None
self, response: httpx.Response, raw_data: dict[str, Any] | None = None
) -> ActionResult:
"""处理 HTTP 错误响应Cubence 专用)"""
status_code = response.status_code
@@ -117,7 +117,7 @@ class CubenceBalanceAction(BalanceAction):
charity_balance = balance_data.get("charity_balance_dollar")
# 窗口限额信息
extra: Dict[str, Any] = {}
extra: dict[str, Any] = {}
# 5小时窗口限额
five_hour = subscription_limits.get("five_hour", {})

View File

@@ -4,7 +4,7 @@ NekoCode 余额查询操作
import time
from datetime import datetime
from typing import Any, Dict, Optional
from typing import Any
import httpx
@@ -122,7 +122,7 @@ class NekoCodeBalanceAction(BalanceAction):
logger.debug(f"解析 effective_start_date 失败: {e}")
# 构建 extra 信息
extra: Dict[str, Any] = {
extra: dict[str, Any] = {
"plan_name": plan_name,
"subscription_status": status,
"daily_quota_limit": daily_quota_limit,
@@ -161,7 +161,7 @@ class NekoCodeBalanceAction(BalanceAction):
)
def _handle_http_error(
self, response: httpx.Response, raw_data: Optional[Dict[str, Any]] = None
self, response: httpx.Response, raw_data: dict[str, Any] | None = None
) -> ActionResult:
"""处理 HTTP 错误响应"""
status_code = response.status_code
@@ -178,7 +178,7 @@ class NekoCodeBalanceAction(BalanceAction):
return super()._handle_http_error(response, raw_data)
@classmethod
def get_config_schema(cls) -> Dict[str, Any]:
def get_config_schema(cls) -> dict[str, Any]:
"""获取操作配置 schema"""
return {
"type": "object",

View File

@@ -3,7 +3,7 @@ New API 余额查询操作
"""
import time
from typing import Any, Dict, Optional
from typing import Any
import httpx
@@ -111,7 +111,7 @@ class NewApiBalanceAction(BalanceAction):
currency=self.config.get("currency", "USD"),
)
async def _do_checkin(self, client: httpx.AsyncClient) -> Optional[Dict[str, Any]]:
async def _do_checkin(self, client: httpx.AsyncClient) -> dict[str, Any] | None:
"""
执行签到(静默,不抛出异常)
@@ -184,7 +184,7 @@ class NewApiBalanceAction(BalanceAction):
return None
@classmethod
def get_config_schema(cls) -> Dict[str, Any]:
def get_config_schema(cls) -> dict[str, Any]:
"""获取操作配置 schema"""
return {
"type": "object",

View File

@@ -4,7 +4,7 @@ YesCode 余额查询操作
import asyncio
from datetime import datetime, timedelta
from typing import Any, Dict
from typing import Any
import httpx
@@ -15,7 +15,7 @@ from src.services.provider_ops.types import ActionResult, ActionStatus, BalanceI
async def fetch_yescode_combined_data(
client: httpx.AsyncClient,
base_url: str,
) -> Dict[str, Any]:
) -> dict[str, Any]:
"""
获取 YesCode 合并数据balance + profile
@@ -31,7 +31,7 @@ async def fetch_yescode_combined_data(
合并后的数据字典
"""
base_url = base_url.rstrip("/")
result: Dict[str, Any] = {}
result: dict[str, Any] = {}
# 并发调用两个接口
balance_task = client.get(f"{base_url}/api/v1/user/balance")
@@ -42,7 +42,7 @@ async def fetch_yescode_combined_data(
)
# 解析 balance 接口
balance_data: Dict[str, Any] = {}
balance_data: dict[str, Any] = {}
if isinstance(balance_resp, httpx.Response) and balance_resp.status_code == 200:
try:
balance_data = balance_resp.json()
@@ -51,7 +51,7 @@ async def fetch_yescode_combined_data(
pass
# 解析 profile 接口
profile_data: Dict[str, Any] = {}
profile_data: dict[str, Any] = {}
if isinstance(profile_resp, httpx.Response) and profile_resp.status_code == 200:
try:
profile_data = profile_resp.json()
@@ -87,7 +87,7 @@ async def fetch_yescode_combined_data(
return result
def parse_yescode_balance_extra(data: Dict[str, Any]) -> Dict[str, Any]:
def parse_yescode_balance_extra(data: dict[str, Any]) -> dict[str, Any]:
"""
解析 YesCode 余额额外信息
@@ -97,7 +97,7 @@ def parse_yescode_balance_extra(data: Dict[str, Any]) -> Dict[str, Any]:
Returns:
统一格式的 extra 字典
"""
extra: Dict[str, Any] = {}
extra: dict[str, Any] = {}
pay_as_you_go = data.get("pay_as_you_go_balance", 0)
subscription = data.get("subscription_balance", 0)

View File

@@ -6,7 +6,7 @@ Anyrouter 架构
import base64
import re
from typing import Any, Dict, List, Optional, Tuple, Type
from typing import Any
import httpx
@@ -118,7 +118,7 @@ def _extract_session_from_cookie(cookie_string: str) -> str:
return cookie_string.strip()
def _parse_session_user_id(cookie_input: str) -> Tuple[Optional[str], Optional[str]]:
def _parse_session_user_id(cookie_input: str) -> tuple[str | None, str | None]:
"""
从 session cookie 中解析用户 ID 和用户名
@@ -213,8 +213,8 @@ def _parse_session_user_id(cookie_input: str) -> Tuple[Optional[str], Optional[s
async def _get_acw_cookie(
base_url: str, timeout: float = 10, proxy: Optional[str] = None
) -> Optional[str]:
base_url: str, timeout: float = 10, proxy: str | None = None
) -> str | None:
"""
获取 acw_sc__v2 Cookie
@@ -230,7 +230,7 @@ async def _get_acw_cookie(
"""
try:
# 构建 client 参数
client_kwargs: Dict[str, Any] = {
client_kwargs: dict[str, Any] = {
"timeout": timeout,
"verify": get_ssl_context(),
}
@@ -278,13 +278,13 @@ class AnyrouterConnector(ProviderConnector):
auth_type = ConnectorAuthType.COOKIE
display_name = "Anyrouter Cookie"
def __init__(self, base_url: str, config: Optional[Dict[str, Any]] = None):
def __init__(self, base_url: str, config: dict[str, Any] | None = None):
super().__init__(base_url, config)
self._session_cookie: Optional[str] = None
self._acw_cookie: Optional[str] = None
self._user_id: Optional[str] = None
self._session_cookie: str | None = None
self._acw_cookie: str | None = None
self._user_id: str | None = None
async def connect(self, credentials: Dict[str, Any]) -> bool:
async def connect(self, credentials: dict[str, Any]) -> bool:
"""建立连接"""
session_cookie = credentials.get("session_cookie")
if not session_cookie:
@@ -336,7 +336,7 @@ class AnyrouterConnector(ProviderConnector):
return request
@classmethod
def get_credentials_schema(cls) -> Dict[str, Any]:
def get_credentials_schema(cls) -> dict[str, Any]:
"""获取凭据配置 schema"""
return {
"type": "object",
@@ -368,13 +368,13 @@ class AnyrouterArchitecture(ProviderArchitecture):
display_name = "Anyrouter"
description = "Anyrouter 中转站预设配置,使用 Cookie 认证"
supported_connectors: List[Type[ProviderConnector]] = [
supported_connectors: list[type[ProviderConnector]] = [
AnyrouterConnector,
]
supported_actions: List[Type[ProviderAction]] = [AnyrouterBalanceAction]
supported_actions: list[type[ProviderAction]] = [AnyrouterBalanceAction]
default_action_configs: Dict[ProviderActionType, Dict[str, Any]] = {
default_action_configs: dict[ProviderActionType, dict[str, Any]] = {
ProviderActionType.QUERY_BALANCE: {
"endpoint": "/api/user/self",
"method": "GET",
@@ -383,7 +383,7 @@ class AnyrouterArchitecture(ProviderArchitecture):
},
}
def get_credentials_schema(self) -> Dict[str, Any]:
def get_credentials_schema(self) -> dict[str, Any]:
"""Anyrouter 使用 session_cookie 认证"""
return AnyrouterConnector.get_credentials_schema()
@@ -394,9 +394,9 @@ class AnyrouterArchitecture(ProviderArchitecture):
async def prepare_verify_config(
self,
base_url: str,
config: Dict[str, Any],
credentials: Dict[str, Any],
) -> Dict[str, Any]:
config: dict[str, Any],
credentials: dict[str, Any],
) -> dict[str, Any]:
"""
验证前获取 acw_sc__v2 Cookie
@@ -417,16 +417,16 @@ class AnyrouterArchitecture(ProviderArchitecture):
def build_verify_headers(
self,
config: Dict[str, Any],
credentials: Dict[str, Any],
) -> Dict[str, str]:
config: dict[str, Any],
credentials: dict[str, Any],
) -> dict[str, str]:
"""
构建 Anyrouter 的验证请求 Headers
使用 Cookie 认证,不使用 Authorization。
同时添加 New-Api-User header。
"""
headers: Dict[str, str] = {}
headers: dict[str, str] = {}
cookies = []
@@ -455,7 +455,7 @@ class AnyrouterArchitecture(ProviderArchitecture):
def parse_verify_response(
self,
status_code: int,
data: Dict[str, Any],
data: dict[str, Any],
) -> VerifyResult:
"""解析 Anyrouter 验证响应"""
if status_code == 401:

View File

@@ -6,7 +6,8 @@ from abc import ABC, abstractmethod
from contextlib import asynccontextmanager
from dataclasses import dataclass
from datetime import datetime, timezone
from typing import Any, AsyncIterator, Dict, List, Optional, Type
from typing import Any
from collections.abc import AsyncIterator
import httpx
@@ -35,7 +36,7 @@ class ProviderConnector(ABC):
auth_type: ConnectorAuthType = ConnectorAuthType.NONE
display_name: str = "Base Connector"
def __init__(self, base_url: str, config: Optional[Dict[str, Any]] = None):
def __init__(self, base_url: str, config: dict[str, Any] | None = None):
"""
初始化连接器
@@ -46,19 +47,19 @@ class ProviderConnector(ABC):
self.base_url = base_url.rstrip("/")
self.config = config or {}
self._status = ConnectorStatus.DISCONNECTED
self._connected_at: Optional[datetime] = None
self._expires_at: Optional[datetime] = None
self._last_error: Optional[str] = None
self._connected_at: datetime | None = None
self._expires_at: datetime | None = None
self._last_error: str | None = None
# 代理配置
self._proxy: Optional[str] = self.config.get("proxy")
self._proxy: str | None = self.config.get("proxy")
# HTTP 客户端配置
self._timeout = self.config.get("timeout", 30)
self._headers: Dict[str, str] = {}
self._headers: dict[str, str] = {}
@abstractmethod
async def connect(self, credentials: Dict[str, Any]) -> bool:
async def connect(self, credentials: dict[str, Any]) -> bool:
"""
建立认证连接
@@ -93,7 +94,7 @@ class ProviderConnector(ABC):
"""
pass
async def refresh_auth(self, credentials: Dict[str, Any]) -> bool:
async def refresh_auth(self, credentials: dict[str, Any]) -> bool:
"""
刷新认证(如 Token 过期)
@@ -144,7 +145,7 @@ class ProviderConnector(ABC):
last_error=self._last_error,
)
def _set_connected(self, expires_at: Optional[datetime] = None) -> None:
def _set_connected(self, expires_at: datetime | None = None) -> None:
"""设置为已连接状态"""
self._status = ConnectorStatus.CONNECTED
self._connected_at = datetime.now(timezone.utc)
@@ -163,7 +164,7 @@ class ProviderConnector(ABC):
self._expires_at = None
@classmethod
def get_credentials_schema(cls) -> Dict[str, Any]:
def get_credentials_schema(cls) -> dict[str, Any]:
"""
获取凭据配置 JSON Schema用于前端表单生成
@@ -180,16 +181,16 @@ class VerifyResult:
"""认证验证结果"""
success: bool
message: Optional[str] = None
username: Optional[str] = None
display_name: Optional[str] = None
email: Optional[str] = None
quota: Optional[float] = None
used_quota: Optional[float] = None
request_count: Optional[int] = None
extra: Optional[Dict[str, Any]] = None
message: str | None = None
username: str | None = None
display_name: str | None = None
email: str | None = None
quota: float | None = None
used_quota: float | None = None
request_count: int | None = None
extra: dict[str, Any] | None = None
def to_dict(self) -> Dict[str, Any]:
def to_dict(self) -> dict[str, Any]:
"""转换为字典"""
if not self.success:
return {"success": False, "message": self.message}
@@ -240,15 +241,15 @@ class ProviderArchitecture(ABC):
description: str = ""
# 支持的 Connector 类型列表(按优先级排序)
supported_connectors: List[Type[ProviderConnector]] = []
supported_connectors: list[type[ProviderConnector]] = []
# 支持的 Action 类型列表
supported_actions: List[Type[ProviderAction]] = []
supported_actions: list[type[ProviderAction]] = []
# 默认操作配置
default_action_configs: Dict[ProviderActionType, Dict[str, Any]] = {}
default_action_configs: dict[ProviderActionType, dict[str, Any]] = {}
def __init__(self, config: Optional[Dict[str, Any]] = None):
def __init__(self, config: dict[str, Any] | None = None):
"""
初始化架构
@@ -260,7 +261,7 @@ class ProviderArchitecture(ABC):
# ==================== 认证验证相关方法(子类必须实现) ====================
@abstractmethod
def get_credentials_schema(self) -> Dict[str, Any]:
def get_credentials_schema(self) -> dict[str, Any]:
"""
获取凭据字段定义JSON Schema 格式)
@@ -303,9 +304,9 @@ class ProviderArchitecture(ABC):
@abstractmethod
def build_verify_headers(
self,
config: Dict[str, Any],
credentials: Dict[str, Any],
) -> Dict[str, str]:
config: dict[str, Any],
credentials: dict[str, Any],
) -> dict[str, str]:
"""
构建认证验证请求的 Headers
@@ -324,7 +325,7 @@ class ProviderArchitecture(ABC):
def parse_verify_response(
self,
status_code: int,
data: Dict[str, Any],
data: dict[str, Any],
) -> VerifyResult:
"""
解析认证验证响应
@@ -345,9 +346,9 @@ class ProviderArchitecture(ABC):
async def prepare_verify_config(
self,
base_url: str,
config: Dict[str, Any],
credentials: Dict[str, Any],
) -> Dict[str, Any]:
config: dict[str, Any],
credentials: dict[str, Any],
) -> dict[str, Any]:
"""
验证前的异步预处理(可选)
@@ -369,8 +370,8 @@ class ProviderArchitecture(ABC):
def get_connector(
self,
base_url: str,
auth_type: Optional[ConnectorAuthType] = None,
config: Optional[Dict[str, Any]] = None,
auth_type: ConnectorAuthType | None = None,
config: dict[str, Any] | None = None,
) -> ProviderConnector:
"""
获取连接器实例
@@ -390,7 +391,7 @@ class ProviderArchitecture(ABC):
raise ValueError(f"架构 {self.architecture_id} 未配置支持的连接器")
# 查找匹配的连接器
connector_cls: Optional[Type[ProviderConnector]] = None
connector_cls: type[ProviderConnector] | None = None
if auth_type:
for cls in self.supported_connectors:
@@ -413,7 +414,7 @@ class ProviderArchitecture(ABC):
def get_action(
self,
action_type: ProviderActionType,
config: Optional[Dict[str, Any]] = None,
config: dict[str, Any] | None = None,
) -> ProviderAction:
"""
获取操作实例
@@ -428,7 +429,7 @@ class ProviderArchitecture(ABC):
Raises:
ValueError: 不支持的操作类型
"""
action_cls: Optional[Type[ProviderAction]] = None
action_cls: type[ProviderAction] | None = None
for cls in self.supported_actions:
if cls.action_type == action_type:
@@ -457,15 +458,15 @@ class ProviderArchitecture(ABC):
"""检查是否支持指定认证类型"""
return any(c.auth_type == auth_type for c in self.supported_connectors)
def get_supported_auth_types(self) -> List[ConnectorAuthType]:
def get_supported_auth_types(self) -> list[ConnectorAuthType]:
"""获取支持的认证类型列表"""
return [c.auth_type for c in self.supported_connectors]
def get_supported_action_types(self) -> List[ProviderActionType]:
def get_supported_action_types(self) -> list[ProviderActionType]:
"""获取支持的操作类型列表"""
return [a.action_type for a in self.supported_actions]
def to_dict(self) -> Dict[str, Any]:
def to_dict(self) -> dict[str, Any]:
"""转换为字典(用于 API 响应)"""
return {
"architecture_id": self.architecture_id,

View File

@@ -4,7 +4,7 @@ Cubence 架构
针对 Cubence 中转站的预设配置。
"""
from typing import Any, Dict, List, Optional, Type
from typing import Any
import httpx
@@ -54,11 +54,11 @@ class CubenceConnector(ProviderConnector):
auth_type = ConnectorAuthType.COOKIE
display_name = "Cubence Cookie"
def __init__(self, base_url: str, config: Optional[Dict[str, Any]] = None):
def __init__(self, base_url: str, config: dict[str, Any] | None = None):
super().__init__(base_url, config)
self._token_cookie: Optional[str] = None
self._token_cookie: str | None = None
async def connect(self, credentials: Dict[str, Any]) -> bool:
async def connect(self, credentials: dict[str, Any]) -> bool:
"""建立连接"""
token_cookie = credentials.get("token_cookie")
if not token_cookie:
@@ -88,7 +88,7 @@ class CubenceConnector(ProviderConnector):
return request
@classmethod
def get_credentials_schema(cls) -> Dict[str, Any]:
def get_credentials_schema(cls) -> dict[str, Any]:
"""获取凭据配置 schema"""
return {
"type": "object",
@@ -120,15 +120,15 @@ class CubenceArchitecture(ProviderArchitecture):
display_name = "Cubence"
description = "Cubence 中转站预设配置,使用 Cookie 认证"
supported_connectors: List[Type[ProviderConnector]] = [
supported_connectors: list[type[ProviderConnector]] = [
CubenceConnector,
]
supported_actions: List[Type[ProviderAction]] = [
supported_actions: list[type[ProviderAction]] = [
CubenceBalanceAction,
]
default_action_configs: Dict[ProviderActionType, Dict[str, Any]] = {
default_action_configs: dict[ProviderActionType, dict[str, Any]] = {
ProviderActionType.QUERY_BALANCE: {
"endpoint": "/api/v1/dashboard/overview",
"method": "GET",
@@ -138,7 +138,7 @@ class CubenceArchitecture(ProviderArchitecture):
},
}
def get_credentials_schema(self) -> Dict[str, Any]:
def get_credentials_schema(self) -> dict[str, Any]:
"""Cubence 使用 token_cookie 认证"""
return CubenceConnector.get_credentials_schema()
@@ -148,15 +148,15 @@ class CubenceArchitecture(ProviderArchitecture):
def build_verify_headers(
self,
config: Dict[str, Any],
credentials: Dict[str, Any],
) -> Dict[str, str]:
config: dict[str, Any],
credentials: dict[str, Any],
) -> dict[str, str]:
"""
构建 Cubence 的验证请求 Headers
使用 Cookie 认证,不使用 Authorization。
"""
headers: Dict[str, str] = {}
headers: dict[str, str] = {}
# 添加 token Cookie
cookie_input = credentials.get("token_cookie")
@@ -169,7 +169,7 @@ class CubenceArchitecture(ProviderArchitecture):
def parse_verify_response(
self,
status_code: int,
data: Dict[str, Any],
data: dict[str, Any],
) -> VerifyResult:
"""解析 Cubence 验证响应"""
if status_code == 401:
@@ -190,7 +190,7 @@ class CubenceArchitecture(ProviderArchitecture):
subscription_limits = user_data.get("subscription_limits", {})
# 构建 extra 信息,包含窗口限额
extra: Dict[str, Any] = {
extra: dict[str, Any] = {
"role": user_info.get("role"),
"invite_code": user_info.get("invite_code"),
}

View File

@@ -39,7 +39,7 @@
3. 在前端 auth-templates/ 添加对应的模板定义
"""
from typing import Any, Dict, List, Optional, Type
from typing import Any
import httpx
@@ -67,14 +67,14 @@ class GenericApiKeyConnector(ProviderConnector):
auth_type = ConnectorAuthType.API_KEY
display_name = "API Key"
def __init__(self, base_url: str, config: Optional[Dict[str, Any]] = None):
def __init__(self, base_url: str, config: dict[str, Any] | None = None):
super().__init__(base_url, config)
self._api_key: Optional[str] = None
self._api_key: str | None = None
# 支持配置认证方式
self._auth_method = self.config.get("auth_method", "bearer")
self._header_name = self.config.get("header_name", "Authorization")
async def connect(self, credentials: Dict[str, Any]) -> bool:
async def connect(self, credentials: dict[str, Any]) -> bool:
"""建立连接"""
api_key = credentials.get("api_key")
if not api_key:
@@ -107,7 +107,7 @@ class GenericApiKeyConnector(ProviderConnector):
return request
@classmethod
def get_credentials_schema(cls) -> Dict[str, Any]:
def get_credentials_schema(cls) -> dict[str, Any]:
"""获取凭据配置 schema"""
return {
"type": "object",
@@ -136,14 +136,14 @@ class GenericApiArchitecture(ProviderArchitecture):
display_name = "通用 API"
description = "可配置的通用 API 架构,适用于各种中转站"
supported_connectors: List[Type[ProviderConnector]] = [
supported_connectors: list[type[ProviderConnector]] = [
GenericApiKeyConnector,
]
supported_actions: List[Type[ProviderAction]] = [NewApiBalanceAction]
supported_actions: list[type[ProviderAction]] = [NewApiBalanceAction]
# 默认操作配置(可被用户配置覆盖)
default_action_configs: Dict[ProviderActionType, Dict[str, Any]] = {
default_action_configs: dict[ProviderActionType, dict[str, Any]] = {
ProviderActionType.QUERY_BALANCE: {
"endpoint": "/api/user/balance",
"method": "GET",
@@ -154,7 +154,7 @@ class GenericApiArchitecture(ProviderArchitecture):
},
}
def get_credentials_schema(self) -> Dict[str, Any]:
def get_credentials_schema(self) -> dict[str, Any]:
"""通用架构只需要 api_key"""
return GenericApiKeyConnector.get_credentials_schema()
@@ -164,11 +164,11 @@ class GenericApiArchitecture(ProviderArchitecture):
def build_verify_headers(
self,
config: Dict[str, Any],
credentials: Dict[str, Any],
) -> Dict[str, str]:
config: dict[str, Any],
credentials: dict[str, Any],
) -> dict[str, str]:
"""构建通用 API 的验证请求 Headers"""
headers: Dict[str, str] = {}
headers: dict[str, str] = {}
api_key = credentials.get("api_key", "")
if api_key:
@@ -184,7 +184,7 @@ class GenericApiArchitecture(ProviderArchitecture):
def parse_verify_response(
self,
status_code: int,
data: Dict[str, Any],
data: dict[str, Any],
) -> VerifyResult:
"""解析通用 API 验证响应"""
if status_code == 401:

View File

@@ -4,7 +4,7 @@ NekoCode 架构
针对 NekoCode 中转站的预设配置,使用 Cookie 认证。
"""
from typing import Any, Dict, List, Optional, Type
from typing import Any
import httpx
@@ -56,11 +56,11 @@ class NekoCodeConnector(ProviderConnector):
auth_type = ConnectorAuthType.COOKIE
display_name = "NekoCode Cookie"
def __init__(self, base_url: str, config: Optional[Dict[str, Any]] = None):
def __init__(self, base_url: str, config: dict[str, Any] | None = None):
super().__init__(base_url, config)
self._session_cookie: Optional[str] = None
self._session_cookie: str | None = None
async def connect(self, credentials: Dict[str, Any]) -> bool:
async def connect(self, credentials: dict[str, Any]) -> bool:
"""建立连接"""
session_cookie = credentials.get("session_cookie")
if not session_cookie:
@@ -90,7 +90,7 @@ class NekoCodeConnector(ProviderConnector):
return request
@classmethod
def get_credentials_schema(cls) -> Dict[str, Any]:
def get_credentials_schema(cls) -> dict[str, Any]:
"""获取凭据配置 schema"""
return {
"type": "object",
@@ -121,20 +121,20 @@ class NekoCodeArchitecture(ProviderArchitecture):
display_name = "NekoCode"
description = "NekoCode 中转站预设配置,使用 Cookie 认证"
supported_connectors: List[Type[ProviderConnector]] = [
supported_connectors: list[type[ProviderConnector]] = [
NekoCodeConnector,
]
supported_actions: List[Type[ProviderAction]] = [NekoCodeBalanceAction]
supported_actions: list[type[ProviderAction]] = [NekoCodeBalanceAction]
default_action_configs: Dict[ProviderActionType, Dict[str, Any]] = {
default_action_configs: dict[ProviderActionType, dict[str, Any]] = {
ProviderActionType.QUERY_BALANCE: {
"endpoint": "/api/usage/summary",
"method": "GET",
},
}
def get_credentials_schema(self) -> Dict[str, Any]:
def get_credentials_schema(self) -> dict[str, Any]:
"""NekoCode 使用 session_cookie 认证"""
return NekoCodeConnector.get_credentials_schema()
@@ -145,9 +145,9 @@ class NekoCodeArchitecture(ProviderArchitecture):
async def prepare_verify_config(
self,
base_url: str,
config: Dict[str, Any],
credentials: Dict[str, Any],
) -> Dict[str, Any]:
config: dict[str, Any],
credentials: dict[str, Any],
) -> dict[str, Any]:
"""
验证前获取 /api/usage/summary 数据(用于显示天卡信息)
@@ -161,7 +161,7 @@ class NekoCodeArchitecture(ProviderArchitecture):
"""
try:
# 构建请求头
headers: Dict[str, str] = {
headers: dict[str, str] = {
"User-Agent": (
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
"AppleWebKit/537.36 (KHTML, like Gecko) "
@@ -176,7 +176,7 @@ class NekoCodeArchitecture(ProviderArchitecture):
headers["Cookie"] = f"session={session_value}"
# 构建 client 参数
client_kwargs: Dict[str, Any] = {
client_kwargs: dict[str, Any] = {
"timeout": 10,
"verify": get_ssl_context(),
}
@@ -202,15 +202,15 @@ class NekoCodeArchitecture(ProviderArchitecture):
def build_verify_headers(
self,
config: Dict[str, Any],
credentials: Dict[str, Any],
) -> Dict[str, str]:
config: dict[str, Any],
credentials: dict[str, Any],
) -> dict[str, str]:
"""
构建 NekoCode 的验证请求 Headers
使用 Cookie 认证,不使用 Authorization。
"""
headers: Dict[str, str] = {}
headers: dict[str, str] = {}
# 添加 session Cookie
cookie_input = credentials.get("session_cookie")
@@ -224,7 +224,7 @@ class NekoCodeArchitecture(ProviderArchitecture):
def parse_verify_response(
self,
status_code: int,
data: Dict[str, Any],
data: dict[str, Any],
) -> VerifyResult:
"""解析 NekoCode 验证响应(/api/user/self + _usage_summary"""
if status_code == 401:
@@ -249,7 +249,7 @@ class NekoCodeArchitecture(ProviderArchitecture):
quota = None
# 从 prepare_verify_config 获取的 _usage_summary 数据(天卡信息)
extra: Dict[str, Any] = {}
extra: dict[str, Any] = {}
usage_summary = data.get("_usage_summary", {})
subscription = usage_summary.get("subscription", {})

View File

@@ -4,7 +4,7 @@ New API 架构
针对 New API 风格的中转站优化的预设配置。
"""
from typing import Any, Dict, List, Optional, Type
from typing import Any
import httpx
@@ -32,13 +32,13 @@ class NewApiConnector(ProviderConnector):
auth_type = ConnectorAuthType.API_KEY
display_name = "New API Key"
def __init__(self, base_url: str, config: Optional[Dict[str, Any]] = None):
def __init__(self, base_url: str, config: dict[str, Any] | None = None):
super().__init__(base_url, config)
self._api_key: Optional[str] = None
self._user_id: Optional[str] = None
self._cookie: Optional[str] = None
self._api_key: str | None = None
self._user_id: str | None = None
self._cookie: str | None = None
async def connect(self, credentials: Dict[str, Any]) -> bool:
async def connect(self, credentials: dict[str, Any]) -> bool:
"""建立连接"""
api_key = credentials.get("api_key")
cookie = credentials.get("cookie")
@@ -85,7 +85,7 @@ class NewApiConnector(ProviderConnector):
return request
@classmethod
def get_credentials_schema(cls) -> Dict[str, Any]:
def get_credentials_schema(cls) -> dict[str, Any]:
"""获取凭据配置 schema"""
return {
"type": "object",
@@ -127,15 +127,15 @@ class NewApiArchitecture(ProviderArchitecture):
display_name = "New API"
description = "New API 风格中转站的预设配置"
supported_connectors: List[Type[ProviderConnector]] = [
supported_connectors: list[type[ProviderConnector]] = [
NewApiConnector,
]
supported_actions: List[Type[ProviderAction]] = [
supported_actions: list[type[ProviderAction]] = [
NewApiBalanceAction,
]
default_action_configs: Dict[ProviderActionType, Dict[str, Any]] = {
default_action_configs: dict[ProviderActionType, dict[str, Any]] = {
ProviderActionType.QUERY_BALANCE: {
"endpoint": "/api/user/self",
"method": "GET",
@@ -144,7 +144,7 @@ class NewApiArchitecture(ProviderArchitecture):
},
}
def get_credentials_schema(self) -> Dict[str, Any]:
def get_credentials_schema(self) -> dict[str, Any]:
"""New API 需要 api_key 和 user_id"""
return NewApiConnector.get_credentials_schema()
@@ -154,15 +154,15 @@ class NewApiArchitecture(ProviderArchitecture):
def build_verify_headers(
self,
config: Dict[str, Any],
credentials: Dict[str, Any],
) -> Dict[str, str]:
config: dict[str, Any],
credentials: dict[str, Any],
) -> dict[str, str]:
"""
构建 New API 的验证请求 Headers
New API 特有:需要 New-Api-User Header 传递用户 ID
"""
headers: Dict[str, str] = {}
headers: dict[str, str] = {}
# Bearer Token 认证
api_key = credentials.get("api_key", "")
@@ -184,7 +184,7 @@ class NewApiArchitecture(ProviderArchitecture):
def parse_verify_response(
self,
status_code: int,
data: Dict[str, Any],
data: dict[str, Any],
) -> VerifyResult:
"""解析 New API 验证响应"""
if status_code == 401:

View File

@@ -4,7 +4,7 @@ One API 架构
针对 One API 风格的中转站优化的预设配置。
"""
from typing import Any, Dict, List, Optional, Type
from typing import Any
import httpx
@@ -29,11 +29,11 @@ class OneApiConnector(ProviderConnector):
auth_type = ConnectorAuthType.API_KEY
display_name = "One API Key"
def __init__(self, base_url: str, config: Optional[Dict[str, Any]] = None):
def __init__(self, base_url: str, config: dict[str, Any] | None = None):
super().__init__(base_url, config)
self._api_key: Optional[str] = None
self._api_key: str | None = None
async def connect(self, credentials: Dict[str, Any]) -> bool:
async def connect(self, credentials: dict[str, Any]) -> bool:
"""建立连接"""
api_key = credentials.get("api_key")
if not api_key:
@@ -60,7 +60,7 @@ class OneApiConnector(ProviderConnector):
return request
@classmethod
def get_credentials_schema(cls) -> Dict[str, Any]:
def get_credentials_schema(cls) -> dict[str, Any]:
"""获取凭据配置 schema"""
return {
"type": "object",
@@ -91,15 +91,15 @@ class OneApiArchitecture(ProviderArchitecture):
display_name = "One API"
description = "One API 风格中转站的预设配置"
supported_connectors: List[Type[ProviderConnector]] = [
supported_connectors: list[type[ProviderConnector]] = [
OneApiConnector,
]
supported_actions: List[Type[ProviderAction]] = [
supported_actions: list[type[ProviderAction]] = [
NewApiBalanceAction,
]
default_action_configs: Dict[ProviderActionType, Dict[str, Any]] = {
default_action_configs: dict[ProviderActionType, dict[str, Any]] = {
ProviderActionType.QUERY_BALANCE: {
"endpoint": "/api/user/self",
"method": "GET",
@@ -110,7 +110,7 @@ class OneApiArchitecture(ProviderArchitecture):
},
}
def get_credentials_schema(self) -> Dict[str, Any]:
def get_credentials_schema(self) -> dict[str, Any]:
"""One API 只需要 api_key"""
return OneApiConnector.get_credentials_schema()
@@ -120,11 +120,11 @@ class OneApiArchitecture(ProviderArchitecture):
def build_verify_headers(
self,
config: Dict[str, Any],
credentials: Dict[str, Any],
) -> Dict[str, str]:
config: dict[str, Any],
credentials: dict[str, Any],
) -> dict[str, str]:
"""构建 One API 的验证请求 Headers"""
headers: Dict[str, str] = {}
headers: dict[str, str] = {}
api_key = credentials.get("api_key", "")
if api_key:
@@ -135,7 +135,7 @@ class OneApiArchitecture(ProviderArchitecture):
def parse_verify_response(
self,
status_code: int,
data: Dict[str, Any],
data: dict[str, Any],
) -> VerifyResult:
"""解析 One API 验证响应"""
if status_code == 401:

View File

@@ -4,7 +4,7 @@ YesCode 架构
针对 YesCode 中转站的预设配置。
"""
from typing import Any, Dict, List, Optional, Type
from typing import Any
import httpx
@@ -23,7 +23,7 @@ from src.services.provider_ops.types import ConnectorAuthType, ProviderActionTyp
from src.utils.ssl_utils import get_ssl_context
def _extract_cookies(cookie_string: str) -> Dict[str, str]:
def _extract_cookies(cookie_string: str) -> dict[str, str]:
"""
从完整的 Cookie 字符串中提取 yescode_auth 和 yescode_csrf
@@ -33,7 +33,7 @@ def _extract_cookies(cookie_string: str) -> Dict[str, str]:
Returns:
包含 yescode_auth 和 yescode_csrf 的字典
"""
result: Dict[str, str] = {}
result: dict[str, str] = {}
for part in cookie_string.split(";"):
part = part.strip()
if "=" in part:
@@ -82,11 +82,11 @@ class YesCodeConnector(ProviderConnector):
auth_type = ConnectorAuthType.COOKIE
display_name = "YesCode Cookie"
def __init__(self, base_url: str, config: Optional[Dict[str, Any]] = None):
def __init__(self, base_url: str, config: dict[str, Any] | None = None):
super().__init__(base_url, config)
self._auth_cookie: Optional[str] = None
self._auth_cookie: str | None = None
async def connect(self, credentials: Dict[str, Any]) -> bool:
async def connect(self, credentials: dict[str, Any]) -> bool:
"""建立连接"""
auth_cookie = credentials.get("auth_cookie")
if not auth_cookie:
@@ -116,7 +116,7 @@ class YesCodeConnector(ProviderConnector):
return request
@classmethod
def get_credentials_schema(cls) -> Dict[str, Any]:
def get_credentials_schema(cls) -> dict[str, Any]:
"""获取凭据配置 schema"""
return {
"type": "object",
@@ -148,15 +148,15 @@ class YesCodeArchitecture(ProviderArchitecture):
display_name = "YesCode"
description = "YesCode 中转站预设配置,使用 Cookie 认证"
supported_connectors: List[Type[ProviderConnector]] = [
supported_connectors: list[type[ProviderConnector]] = [
YesCodeConnector,
]
supported_actions: List[Type[ProviderAction]] = [
supported_actions: list[type[ProviderAction]] = [
YesCodeBalanceAction,
]
default_action_configs: Dict[ProviderActionType, Dict[str, Any]] = {
default_action_configs: dict[ProviderActionType, dict[str, Any]] = {
ProviderActionType.QUERY_BALANCE: {
"endpoint": "/api/v1/user/balance",
"method": "GET",
@@ -166,7 +166,7 @@ class YesCodeArchitecture(ProviderArchitecture):
},
}
def get_credentials_schema(self) -> Dict[str, Any]:
def get_credentials_schema(self) -> dict[str, Any]:
"""YesCode 使用 auth_cookie 认证"""
return YesCodeConnector.get_credentials_schema()
@@ -177,15 +177,15 @@ class YesCodeArchitecture(ProviderArchitecture):
async def prepare_verify_config(
self,
base_url: str,
config: Dict[str, Any],
credentials: Dict[str, Any],
) -> Dict[str, Any]:
config: dict[str, Any],
credentials: dict[str, Any],
) -> dict[str, Any]:
"""
预获取合并数据balance + profile
验证时并发调用两个接口获取完整数据。
"""
extra_config: Dict[str, Any] = {}
extra_config: dict[str, Any] = {}
cookie_input = credentials.get("auth_cookie")
if not cookie_input:
@@ -198,7 +198,7 @@ class YesCodeArchitecture(ProviderArchitecture):
try:
# 构建 client 参数
client_kwargs: Dict[str, Any] = {
client_kwargs: dict[str, Any] = {
"headers": {"Cookie": cookie_header},
"timeout": 10.0,
"verify": get_ssl_context(),
@@ -218,15 +218,15 @@ class YesCodeArchitecture(ProviderArchitecture):
def build_verify_headers(
self,
config: Dict[str, Any],
credentials: Dict[str, Any],
) -> Dict[str, str]:
config: dict[str, Any],
credentials: dict[str, Any],
) -> dict[str, str]:
"""
构建 YesCode 的验证请求 Headers
使用 Cookie 认证,不使用 Authorization。
"""
headers: Dict[str, str] = {}
headers: dict[str, str] = {}
# 添加 Cookie
cookie_input = credentials.get("auth_cookie")
@@ -238,7 +238,7 @@ class YesCodeArchitecture(ProviderArchitecture):
def parse_verify_response(
self,
status_code: int,
data: Dict[str, Any],
data: dict[str, Any],
) -> VerifyResult:
"""解析 YesCode 验证响应(使用预获取的合并数据)"""
if status_code == 401:

View File

@@ -4,8 +4,9 @@
管理所有可用的 Provider 架构。
"""
from __future__ import annotations
import threading
from typing import Dict, List, Optional, Type
from src.core.logger import logger
from src.services.provider_ops.architectures import (
@@ -27,11 +28,11 @@ class ArchitectureRegistry:
单例模式,管理所有可用的 Provider 架构。
"""
_instance: Optional["ArchitectureRegistry"] = None
_instance: ArchitectureRegistry | None = None
_lock = threading.Lock()
_initialized: bool = False
def __new__(cls) -> "ArchitectureRegistry":
def __new__(cls) -> ArchitectureRegistry:
if cls._instance is None:
with cls._lock:
if cls._instance is None:
@@ -43,7 +44,7 @@ class ArchitectureRegistry:
if self._initialized:
return
self._architectures: Dict[str, ProviderArchitecture] = {}
self._architectures: dict[str, ProviderArchitecture] = {}
self._initialized = True
# 注册内置架构
@@ -51,7 +52,7 @@ class ArchitectureRegistry:
def _register_builtin_architectures(self) -> None:
"""注册内置架构"""
builtin: List[Type[ProviderArchitecture]] = [
builtin: list[type[ProviderArchitecture]] = [
AnyrouterArchitecture,
CubenceArchitecture,
GenericApiArchitecture,
@@ -92,7 +93,7 @@ class ArchitectureRegistry:
return True
return False
def get(self, architecture_id: str) -> Optional[ProviderArchitecture]:
def get(self, architecture_id: str) -> ProviderArchitecture | None:
"""
获取架构
@@ -104,7 +105,7 @@ class ArchitectureRegistry:
"""
return self._architectures.get(architecture_id)
def get_or_default(self, architecture_id: Optional[str] = None) -> ProviderArchitecture:
def get_or_default(self, architecture_id: str | None = None) -> ProviderArchitecture:
"""
获取架构,如果不存在则返回默认架构
@@ -120,21 +121,21 @@ class ArchitectureRegistry:
# 返回默认架构generic_api
return self._architectures.get("generic_api", GenericApiArchitecture())
def list_all(self) -> List[ProviderArchitecture]:
def list_all(self) -> list[ProviderArchitecture]:
"""获取所有已注册的架构"""
return list(self._architectures.values())
def list_ids(self) -> List[str]:
def list_ids(self) -> list[str]:
"""获取所有已注册的架构 ID"""
return list(self._architectures.keys())
def to_dict_list(self) -> List[Dict]:
def to_dict_list(self) -> list[dict]:
"""获取所有架构的字典表示(用于 API 响应)"""
return [arch.to_dict() for arch in self._architectures.values()]
# 全局注册表实例
_registry: Optional[ArchitectureRegistry] = None
_registry: ArchitectureRegistry | None = None
def get_registry() -> ArchitectureRegistry:

View File

@@ -8,7 +8,7 @@ import asyncio
import os
from dataclasses import asdict
from datetime import datetime, timezone
from typing import Any, Dict, List, Optional
from typing import Any
from sqlalchemy.orm import Session
@@ -18,7 +18,7 @@ from src.core.crypto import CryptoService
from src.core.logger import logger
from src.database import create_session
from src.models.database import Provider
from src.services.provider_ops.architectures import ProviderArchitecture, ProviderConnector
from src.services.provider_ops.architectures import ProviderConnector
from src.services.provider_ops.registry import get_registry
from src.services.provider_ops.types import (
ActionResult,
@@ -85,11 +85,11 @@ class ProviderOpsService:
self.crypto = CryptoService()
# 连接器缓存 {provider_id: ProviderConnector}
self._connectors: Dict[str, ProviderConnector] = {}
self._connectors: dict[str, ProviderConnector] = {}
# ==================== 配置管理 ====================
def get_config(self, provider_id: str) -> Optional[ProviderOpsConfig]:
def get_config(self, provider_id: str) -> ProviderOpsConfig | None:
"""
获取 Provider 的操作配置
@@ -186,7 +186,7 @@ class ProviderOpsService:
async def connect(
self,
provider_id: str,
credentials: Optional[Dict[str, Any]] = None,
credentials: dict[str, Any] | None = None,
) -> tuple[bool, str]:
"""
建立与 Provider 的连接
@@ -296,7 +296,7 @@ class ProviderOpsService:
self,
provider_id: str,
action_type: ProviderActionType,
action_config: Optional[Dict[str, Any]] = None,
action_config: dict[str, Any] | None = None,
) -> ActionResult:
"""
执行操作
@@ -371,7 +371,7 @@ class ProviderOpsService:
async def query_balance(
self,
provider_id: str,
config: Optional[Dict[str, Any]] = None,
config: dict[str, Any] | None = None,
) -> ActionResult:
"""
查询余额(快捷方法)
@@ -508,7 +508,7 @@ class ProviderOpsService:
self,
provider_id: str,
quota_usd: float,
extra: Optional[Dict[str, Any]] = None,
extra: dict[str, Any] | None = None,
) -> None:
"""
从验证结果缓存余额
@@ -538,7 +538,7 @@ class ProviderOpsService:
await CacheService.set(cache_key, cache_data, BALANCE_CACHE_TTL)
logger.debug(f"验证成功,缓存余额: provider_id={provider_id}, quota_usd={quota_usd}")
async def _get_cached_balance(self, provider_id: str) -> Optional[ActionResult]:
async def _get_cached_balance(self, provider_id: str) -> ActionResult | None:
"""获取缓存的余额"""
cache_key = f"provider_ops:balance:{provider_id}"
cached = await CacheService.get(cache_key)
@@ -585,7 +585,7 @@ class ProviderOpsService:
async def checkin(
self,
provider_id: str,
config: Optional[Dict[str, Any]] = None,
config: dict[str, Any] | None = None,
) -> ActionResult:
"""
签到(快捷方法)
@@ -601,11 +601,11 @@ class ProviderOpsService:
# ==================== 辅助方法 ====================
def _get_provider(self, provider_id: str) -> Optional[Provider]:
def _get_provider(self, provider_id: str) -> Provider | None:
"""获取 Provider"""
return self.db.query(Provider).filter(Provider.id == provider_id).first()
def _get_provider_base_url(self, provider: Provider) -> Optional[str]:
def _get_provider_base_url(self, provider: Provider) -> str | None:
"""从 Provider 获取 base_url"""
# 优先从第一个 endpoint 获取
if provider.endpoints:
@@ -624,7 +624,7 @@ class ProviderOpsService:
return None
def _encrypt_credentials(self, credentials: Dict[str, Any]) -> Dict[str, Any]:
def _encrypt_credentials(self, credentials: dict[str, Any]) -> dict[str, Any]:
"""加密凭据中的敏感字段"""
encrypted = {}
for key, value in credentials.items():
@@ -639,7 +639,7 @@ class ProviderOpsService:
encrypted[key] = value
return encrypted
def _decrypt_credentials(self, credentials: Dict[str, Any]) -> Dict[str, Any]:
def _decrypt_credentials(self, credentials: dict[str, Any]) -> dict[str, Any]:
"""解密凭据中的敏感字段"""
decrypted = {}
for key, value in credentials.items():
@@ -653,7 +653,7 @@ class ProviderOpsService:
decrypted[key] = value
return decrypted
def get_masked_credentials(self, credentials: Dict[str, Any]) -> Dict[str, Any]:
def get_masked_credentials(self, credentials: dict[str, Any]) -> dict[str, Any]:
"""
获取脱敏后的凭据
@@ -683,8 +683,8 @@ class ProviderOpsService:
def merge_credentials_with_saved(
self,
provider_id: str,
credentials: Dict[str, Any],
) -> Dict[str, Any]:
credentials: dict[str, Any],
) -> dict[str, Any]:
"""
合并凭据:如果请求中的敏感字段为空,使用已保存的凭据
@@ -720,8 +720,8 @@ class ProviderOpsService:
# ==================== 批量操作 ====================
async def batch_query_balance(
self, provider_ids: Optional[List[str]] = None
) -> Dict[str, ActionResult]:
self, provider_ids: list[str] | None = None
) -> dict[str, ActionResult]:
"""
批量查询余额(优先返回缓存,后台异步刷新)
@@ -780,10 +780,10 @@ class ProviderOpsService:
base_url: str,
architecture_id: str,
auth_type: ConnectorAuthType,
config: Dict[str, Any],
credentials: Dict[str, Any],
provider_id: Optional[str] = None,
) -> Dict[str, Any]:
config: dict[str, Any],
credentials: dict[str, Any],
provider_id: str | None = None,
) -> dict[str, Any]:
"""
验证认证配置
@@ -831,7 +831,7 @@ class ProviderOpsService:
try:
# 构建 httpx client 参数
client_kwargs: Dict[str, Any] = {
client_kwargs: dict[str, Any] = {
"timeout": 30.0,
"verify": get_ssl_context(),
}

View File

@@ -2,10 +2,12 @@
Provider 操作模块类型定义
"""
from __future__ import annotations
from dataclasses import dataclass, field
from datetime import datetime, timezone
from enum import Enum
from typing import Any, Dict, List, Optional
from typing import Any
class ConnectorAuthType(str, Enum):
@@ -60,23 +62,23 @@ class ConnectorStatus(str, Enum):
class BalanceInfo:
"""余额信息"""
total_granted: Optional[float] = None # 总授予额度
total_used: Optional[float] = None # 已使用额度
total_available: Optional[float] = None # 可用余额
expires_at: Optional[datetime] = None # 过期时间
total_granted: float | None = None # 总授予额度
total_used: float | None = None # 已使用额度
total_available: float | None = None # 可用余额
expires_at: datetime | None = None # 过期时间
currency: str = "USD" # 货币单位
extra: Dict[str, Any] = field(default_factory=dict) # 额外信息
extra: dict[str, Any] = field(default_factory=dict) # 额外信息
@dataclass
class CheckinInfo:
"""签到信息"""
reward: Optional[float] = None # 奖励额度
streak_days: Optional[int] = None # 连续签到天数
next_reward: Optional[float] = None # 下次奖励
message: Optional[str] = None # 签到消息
extra: Dict[str, Any] = field(default_factory=dict)
reward: float | None = None # 奖励额度
streak_days: int | None = None # 连续签到天数
next_reward: float | None = None # 下次奖励
message: str | None = None # 签到消息
extra: dict[str, Any] = field(default_factory=dict)
@dataclass
@@ -85,13 +87,13 @@ class ActionResult:
status: ActionStatus
action_type: ProviderActionType
data: Optional[Any] = None # 操作返回的数据(如 BalanceInfo, CheckinInfo
message: Optional[str] = None # 消息
data: Any | None = None # 操作返回的数据(如 BalanceInfo, CheckinInfo
message: str | None = None # 消息
executed_at: datetime = field(default_factory=lambda: datetime.now(timezone.utc))
response_time_ms: Optional[int] = None # 响应时间(毫秒)
raw_response: Optional[Dict[str, Any]] = None # 原始响应(调试用)
response_time_ms: int | None = None # 响应时间(毫秒)
raw_response: dict[str, Any] | None = None # 原始响应(调试用)
cache_ttl_seconds: int = 300 # 建议缓存时间
retry_after_seconds: Optional[int] = None # 失败后重试间隔
retry_after_seconds: int | None = None # 失败后重试间隔
@property
def is_success(self) -> bool:
@@ -104,10 +106,10 @@ class ConnectorState:
status: ConnectorStatus
auth_type: ConnectorAuthType
connected_at: Optional[datetime] = None
expires_at: Optional[datetime] = None
last_error: Optional[str] = None
extra: Dict[str, Any] = field(default_factory=dict)
connected_at: datetime | None = None
expires_at: datetime | None = None
last_error: str | None = None
extra: dict[str, Any] = field(default_factory=dict)
@dataclass
@@ -115,21 +117,21 @@ class ProviderOpsConfig:
"""Provider 操作配置(存储在 Provider.config['provider_ops'] 中)"""
architecture_id: str = "generic_api"
base_url: Optional[str] = None # API 基础地址
base_url: str | None = None # API 基础地址
# 连接器配置
connector_auth_type: ConnectorAuthType = ConnectorAuthType.API_KEY
connector_config: Dict[str, Any] = field(default_factory=dict)
connector_credentials: Dict[str, Any] = field(default_factory=dict) # 加密存储
connector_config: dict[str, Any] = field(default_factory=dict)
connector_credentials: dict[str, Any] = field(default_factory=dict) # 加密存储
# 操作配置
actions: Dict[str, Dict[str, Any]] = field(default_factory=dict)
actions: dict[str, dict[str, Any]] = field(default_factory=dict)
# 定时任务配置
schedule: Dict[str, str] = field(default_factory=dict) # {action_type: cron_expression}
schedule: dict[str, str] = field(default_factory=dict) # {action_type: cron_expression}
@classmethod
def from_dict(cls, data: Optional[Dict[str, Any]]) -> "ProviderOpsConfig":
def from_dict(cls, data: dict[str, Any] | None) -> ProviderOpsConfig:
"""从字典创建配置"""
if not data:
return cls()
@@ -146,7 +148,7 @@ class ProviderOpsConfig:
schedule=data.get("schedule", {}),
)
def to_dict(self) -> Dict[str, Any]:
def to_dict(self) -> dict[str, Any]:
"""转换为字典(用于存储)"""
return {
"architecture_id": self.architecture_id,