mirror of
https://github.com/fawney19/Aether.git
synced 2026-09-03 09:50:21 +08:00
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:
@@ -6,10 +6,9 @@
|
||||
from collections import defaultdict
|
||||
from dataclasses import dataclass
|
||||
from datetime import datetime, timedelta, timezone
|
||||
from typing import Dict, List, Optional
|
||||
|
||||
from fastapi import APIRouter, Depends, Query, Request
|
||||
from sqlalchemy import and_, func, or_
|
||||
from sqlalchemy import and_, or_
|
||||
from sqlalchemy.orm import Session, joinedload
|
||||
|
||||
from src.api.base.adapter import ApiAdapter, ApiMode
|
||||
@@ -41,10 +40,10 @@ router = APIRouter(prefix="/api/public", tags=["System Catalog"])
|
||||
pipeline = ApiRequestPipeline()
|
||||
|
||||
|
||||
@router.get("/providers", response_model=List[PublicProviderResponse])
|
||||
@router.get("/providers", response_model=list[PublicProviderResponse])
|
||||
async def get_public_providers(
|
||||
request: Request,
|
||||
is_active: Optional[bool] = Query(None, description="过滤活跃状态"),
|
||||
is_active: bool | None = Query(None, description="过滤活跃状态"),
|
||||
skip: int = Query(0, description="跳过记录数"),
|
||||
limit: int = Query(100, description="返回记录数限制"),
|
||||
db: Session = Depends(get_db),
|
||||
@@ -77,11 +76,11 @@ async def get_public_providers(
|
||||
return await pipeline.run(adapter=adapter, http_request=request, db=db, mode=ApiMode.PUBLIC)
|
||||
|
||||
|
||||
@router.get("/models", response_model=List[PublicModelResponse])
|
||||
@router.get("/models", response_model=list[PublicModelResponse])
|
||||
async def get_public_models(
|
||||
request: Request,
|
||||
provider_id: Optional[str] = Query(None, description="提供商ID过滤"),
|
||||
is_active: Optional[bool] = Query(None, description="过滤活跃状态"),
|
||||
provider_id: str | None = Query(None, description="提供商ID过滤"),
|
||||
is_active: bool | None = Query(None, description="过滤活跃状态"),
|
||||
skip: int = Query(0, description="跳过记录数"),
|
||||
limit: int = Query(100, description="返回记录数限制"),
|
||||
db: Session = Depends(get_db),
|
||||
@@ -145,7 +144,7 @@ async def get_public_stats(request: Request, db: Session = Depends(get_db)):
|
||||
async def search_models(
|
||||
request: Request,
|
||||
q: str = Query(..., description="搜索关键词"),
|
||||
provider_id: Optional[int] = Query(None, description="提供商ID过滤"),
|
||||
provider_id: int | None = Query(None, description="提供商ID过滤"),
|
||||
limit: int = Query(20, description="返回记录数限制"),
|
||||
db: Session = Depends(get_db),
|
||||
):
|
||||
@@ -234,8 +233,8 @@ async def get_public_global_models(
|
||||
request: Request,
|
||||
skip: int = Query(0, ge=0, description="跳过记录数"),
|
||||
limit: int = Query(100, ge=1, le=1000, description="返回记录数限制"),
|
||||
is_active: Optional[bool] = Query(None, description="过滤活跃状态"),
|
||||
search: Optional[str] = Query(None, description="搜索关键词"),
|
||||
is_active: bool | None = Query(None, description="过滤活跃状态"),
|
||||
search: str | None = Query(None, description="搜索关键词"),
|
||||
db: Session = Depends(get_db),
|
||||
):
|
||||
"""
|
||||
@@ -283,7 +282,7 @@ class PublicApiAdapter(ApiAdapter):
|
||||
|
||||
@dataclass
|
||||
class PublicProvidersAdapter(PublicApiAdapter):
|
||||
is_active: Optional[bool]
|
||||
is_active: bool | None
|
||||
skip: int
|
||||
limit: int
|
||||
|
||||
@@ -338,8 +337,8 @@ class PublicProvidersAdapter(PublicApiAdapter):
|
||||
|
||||
@dataclass
|
||||
class PublicModelsAdapter(PublicApiAdapter):
|
||||
provider_id: Optional[str]
|
||||
is_active: Optional[bool]
|
||||
provider_id: str | None
|
||||
is_active: bool | None
|
||||
skip: int
|
||||
limit: int
|
||||
|
||||
@@ -426,7 +425,7 @@ class PublicStatsAdapter(PublicApiAdapter):
|
||||
@dataclass
|
||||
class PublicSearchModelsAdapter(PublicApiAdapter):
|
||||
query: str
|
||||
provider_id: Optional[int]
|
||||
provider_id: int | None
|
||||
limit: int
|
||||
|
||||
async def handle(self, context): # type: ignore[override]
|
||||
@@ -508,7 +507,7 @@ class PublicApiFormatHealthMonitorAdapter(PublicApiAdapter):
|
||||
.all()
|
||||
)
|
||||
|
||||
all_formats: List[str] = []
|
||||
all_formats: list[str] = []
|
||||
for (api_format_enum,) in active_formats:
|
||||
api_format = (
|
||||
api_format_enum.value if hasattr(api_format_enum, "value") else str(api_format_enum)
|
||||
@@ -525,7 +524,7 @@ class PublicApiFormatHealthMonitorAdapter(PublicApiAdapter):
|
||||
)
|
||||
.all()
|
||||
)
|
||||
endpoint_map: Dict[str, List[str]] = defaultdict(list)
|
||||
endpoint_map: dict[str, list[str]] = defaultdict(list)
|
||||
for api_format_enum, endpoint_id in endpoint_rows:
|
||||
api_format = (
|
||||
api_format_enum.value if hasattr(api_format_enum, "value") else str(api_format_enum)
|
||||
@@ -551,7 +550,7 @@ class PublicApiFormatHealthMonitorAdapter(PublicApiAdapter):
|
||||
.all()
|
||||
)
|
||||
|
||||
grouped_candidates: Dict[str, List[RequestCandidate]] = {}
|
||||
grouped_candidates: dict[str, list[RequestCandidate]] = {}
|
||||
|
||||
for candidate, api_format_enum in rows:
|
||||
api_format = (
|
||||
@@ -564,7 +563,7 @@ class PublicApiFormatHealthMonitorAdapter(PublicApiAdapter):
|
||||
grouped_candidates[api_format].append(candidate)
|
||||
|
||||
# 3. 为所有活跃格式生成监控数据
|
||||
monitors: List[PublicApiFormatHealthMonitor] = []
|
||||
monitors: list[PublicApiFormatHealthMonitor] = []
|
||||
for api_format in all_formats:
|
||||
candidates = grouped_candidates.get(api_format, [])
|
||||
|
||||
@@ -579,7 +578,7 @@ class PublicApiFormatHealthMonitorAdapter(PublicApiAdapter):
|
||||
success_rate = success_count / actual_completed if actual_completed > 0 else 1.0
|
||||
|
||||
# 转换为公开版事件列表(不含敏感信息如 provider_id, key_id)
|
||||
events: List[PublicHealthEvent] = []
|
||||
events: list[PublicHealthEvent] = []
|
||||
for c in candidates:
|
||||
event_time = c.finished_at or c.started_at or c.created_at
|
||||
events.append(
|
||||
@@ -649,8 +648,8 @@ class PublicGlobalModelsAdapter(PublicApiAdapter):
|
||||
|
||||
skip: int
|
||||
limit: int
|
||||
is_active: Optional[bool]
|
||||
search: Optional[str]
|
||||
is_active: bool | None
|
||||
search: str | None
|
||||
|
||||
async def handle(self, context): # type: ignore[override]
|
||||
db = context.db
|
||||
|
||||
@@ -7,8 +7,6 @@
|
||||
- Authorization: Bearer (bearer) -> OpenAI 格式
|
||||
"""
|
||||
|
||||
from typing import Optional, Tuple, Union
|
||||
|
||||
from fastapi import APIRouter, Depends, Query, Request
|
||||
from fastapi.responses import JSONResponse
|
||||
from sqlalchemy.orm import Session
|
||||
@@ -35,7 +33,6 @@ from src.core.logger import logger
|
||||
from src.database import get_db
|
||||
from src.models.database import ApiKey, User
|
||||
from src.services.auth.service import AuthService
|
||||
from src.services.system.config import SystemConfigService
|
||||
|
||||
router = APIRouter(tags=["System Catalog"])
|
||||
|
||||
@@ -54,7 +51,7 @@ _ALL_CHAT_FORMATS = [
|
||||
|
||||
def _extract_api_key_from_request(
|
||||
request: Request, definition: ApiFormatDefinition
|
||||
) -> Optional[str]:
|
||||
) -> str | None:
|
||||
"""根据格式定义从请求中提取 API Key"""
|
||||
auth_header = definition.auth_header.lower()
|
||||
auth_type = definition.auth_type
|
||||
@@ -76,7 +73,7 @@ def _extract_api_key_from_request(
|
||||
return header_value
|
||||
|
||||
|
||||
def _detect_api_format_and_key(request: Request) -> Tuple[str, Optional[str]]:
|
||||
def _detect_api_format_and_key(request: Request) -> tuple[str, str | None]:
|
||||
"""
|
||||
根据请求头检测 API 格式并提取 API Key
|
||||
|
||||
@@ -163,7 +160,7 @@ def _build_empty_list_response(api_format: str) -> dict:
|
||||
|
||||
def _filter_formats_by_restrictions(
|
||||
formats: list[str], restrictions: AccessRestrictions, api_format: str
|
||||
) -> Tuple[list[str], Optional[dict]]:
|
||||
) -> tuple[list[str], dict | None]:
|
||||
"""
|
||||
根据访问限制过滤 API 格式
|
||||
|
||||
@@ -182,7 +179,7 @@ def _filter_formats_by_restrictions(
|
||||
return filtered, None
|
||||
|
||||
|
||||
def _authenticate(db: Session, api_key: Optional[str]) -> Tuple[Optional[User], Optional[ApiKey]]:
|
||||
def _authenticate(db: Session, api_key: str | None) -> tuple[User | None, ApiKey | None]:
|
||||
"""
|
||||
认证 API Key
|
||||
|
||||
@@ -248,8 +245,8 @@ def _build_auth_error_response(api_format: str) -> JSONResponse:
|
||||
|
||||
def _build_claude_list_response(
|
||||
models: list[ModelInfo],
|
||||
before_id: Optional[str],
|
||||
after_id: Optional[str],
|
||||
before_id: str | None,
|
||||
after_id: str | None,
|
||||
limit: int,
|
||||
) -> dict:
|
||||
"""构建 Claude 格式的列表响应"""
|
||||
@@ -309,7 +306,7 @@ def _build_openai_list_response(models: list[ModelInfo]) -> dict:
|
||||
def _build_gemini_list_response(
|
||||
models: list[ModelInfo],
|
||||
page_size: int,
|
||||
page_token: Optional[str],
|
||||
page_token: str | None,
|
||||
) -> dict:
|
||||
"""构建 Gemini 格式的列表响应"""
|
||||
# 处理分页
|
||||
@@ -435,14 +432,14 @@ def _build_404_response(model_id: str, api_format: str) -> JSONResponse:
|
||||
async def list_models(
|
||||
request: Request,
|
||||
# Claude 分页参数
|
||||
before_id: Optional[str] = Query(None, description="返回此 ID 之前的结果 (Claude)"),
|
||||
after_id: Optional[str] = Query(None, description="返回此 ID 之后的结果 (Claude)"),
|
||||
before_id: str | None = Query(None, description="返回此 ID 之前的结果 (Claude)"),
|
||||
after_id: str | None = Query(None, description="返回此 ID 之后的结果 (Claude)"),
|
||||
limit: int = Query(20, ge=1, le=1000, description="返回数量限制 (Claude)"),
|
||||
# Gemini 分页参数
|
||||
page_size: int = Query(50, alias="pageSize", ge=1, le=1000, description="每页数量 (Gemini)"),
|
||||
page_token: Optional[str] = Query(None, alias="pageToken", description="分页 token (Gemini)"),
|
||||
page_token: str | None = Query(None, alias="pageToken", description="分页 token (Gemini)"),
|
||||
db: Session = Depends(get_db),
|
||||
) -> Union[dict, JSONResponse]:
|
||||
) -> dict | JSONResponse:
|
||||
"""
|
||||
列出可用模型(统一端点)
|
||||
|
||||
@@ -556,7 +553,7 @@ async def retrieve_model(
|
||||
model_id: str,
|
||||
request: Request,
|
||||
db: Session = Depends(get_db),
|
||||
) -> Union[dict, JSONResponse]:
|
||||
) -> dict | JSONResponse:
|
||||
"""
|
||||
获取单个模型详情(统一端点)
|
||||
|
||||
@@ -658,9 +655,9 @@ async def retrieve_model(
|
||||
async def list_models_gemini(
|
||||
request: Request,
|
||||
page_size: int = Query(50, alias="pageSize", ge=1, le=1000),
|
||||
page_token: Optional[str] = Query(None, alias="pageToken"),
|
||||
page_token: str | None = Query(None, alias="pageToken"),
|
||||
db: Session = Depends(get_db),
|
||||
) -> Union[dict, JSONResponse]:
|
||||
) -> dict | JSONResponse:
|
||||
"""
|
||||
列出可用模型(Gemini v1beta 专用端点)
|
||||
|
||||
@@ -741,7 +738,7 @@ async def get_model_gemini(
|
||||
request: Request,
|
||||
model_name: str,
|
||||
db: Session = Depends(get_db),
|
||||
) -> Union[dict, JSONResponse]:
|
||||
) -> dict | JSONResponse:
|
||||
"""
|
||||
获取单个模型详情(Gemini v1beta 专用端点)
|
||||
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
"""公开模块状态 API(供登录页等使用)"""
|
||||
|
||||
from typing import List
|
||||
|
||||
from fastapi import APIRouter, Depends
|
||||
from pydantic import BaseModel
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from src.core.modules import ModuleCategory, get_module_registry
|
||||
from src.core.modules import get_module_registry
|
||||
from src.database import get_db
|
||||
|
||||
router = APIRouter(prefix="/api/modules", tags=["Modules"])
|
||||
@@ -20,7 +19,7 @@ class AuthModuleInfo(BaseModel):
|
||||
active: bool
|
||||
|
||||
|
||||
@router.get("/auth-status", response_model=List[AuthModuleInfo])
|
||||
@router.get("/auth-status", response_model=list[AuthModuleInfo])
|
||||
async def get_auth_modules_status(db: Session = Depends(get_db)):
|
||||
"""
|
||||
获取认证模块状态(公开接口)
|
||||
|
||||
@@ -5,7 +5,7 @@ System Catalog / 健康检查相关端点
|
||||
"""
|
||||
|
||||
from datetime import datetime, timezone
|
||||
from typing import Any, Dict, Optional
|
||||
from typing import Any
|
||||
|
||||
import httpx
|
||||
from fastapi import APIRouter, Depends, HTTPException, Query, Request
|
||||
@@ -28,7 +28,7 @@ router = APIRouter(tags=["System Catalog"])
|
||||
# ============== 辅助函数 ==============
|
||||
|
||||
|
||||
def _as_bool(value: Optional[str], default: bool) -> bool:
|
||||
def _as_bool(value: str | None, default: bool) -> bool:
|
||||
"""将字符串转换为布尔值"""
|
||||
if value is None:
|
||||
return default
|
||||
@@ -39,9 +39,9 @@ def _serialize_provider(
|
||||
provider: Provider,
|
||||
include_models: bool,
|
||||
include_endpoints: bool,
|
||||
) -> Dict[str, Any]:
|
||||
) -> dict[str, Any]:
|
||||
"""序列化 Provider 对象"""
|
||||
provider_data: Dict[str, Any] = {
|
||||
provider_data: dict[str, Any] = {
|
||||
"id": provider.id,
|
||||
"name": provider.name,
|
||||
"is_active": provider.is_active,
|
||||
@@ -81,7 +81,7 @@ def _serialize_provider(
|
||||
return provider_data
|
||||
|
||||
|
||||
def _select_provider(db: Session, provider_name: Optional[str]) -> Optional[Provider]:
|
||||
def _select_provider(db: Session, provider_name: str | None) -> Provider | None:
|
||||
"""选择 Provider(按 provider_priority 优先级选择)"""
|
||||
query = db.query(Provider).filter(Provider.is_active == True)
|
||||
if provider_name:
|
||||
@@ -104,7 +104,7 @@ async def service_health(db: Session = Depends(get_db)):
|
||||
)
|
||||
active_models = db.query(func.count(Model.id)).filter(Model.is_active == True).scalar() or 0
|
||||
|
||||
redis_info: Dict[str, Any] = {"status": "unknown"}
|
||||
redis_info: dict[str, Any] = {"status": "unknown"}
|
||||
try:
|
||||
redis = await get_redis_client()
|
||||
if redis:
|
||||
@@ -245,9 +245,9 @@ async def provider_detail(
|
||||
async def test_connection(
|
||||
request: Request,
|
||||
db: Session = Depends(get_db),
|
||||
provider: Optional[str] = Query(None),
|
||||
provider: str | None = Query(None),
|
||||
model: str = Query("claude-3-haiku-20240307"),
|
||||
api_format: Optional[str] = Query(None),
|
||||
api_format: str | None = Query(None),
|
||||
):
|
||||
"""测试 Provider 连接"""
|
||||
selected_provider = _select_provider(db, provider)
|
||||
|
||||
Reference in New Issue
Block a user