2025-12-10 20:52:44 +08:00
|
|
|
|
"""
|
2026-01-10 18:43:53 +08:00
|
|
|
|
Key RPM 限制管理 API
|
2025-12-10 20:52:44 +08:00
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
|
|
from dataclasses import dataclass
|
2026-02-01 17:28:00 +08:00
|
|
|
|
from typing import Any
|
2025-12-10 20:52:44 +08:00
|
|
|
|
|
|
|
|
|
|
from fastapi import APIRouter, Depends, Request
|
|
|
|
|
|
from sqlalchemy.orm import Session
|
|
|
|
|
|
|
|
|
|
|
|
from src.api.base.admin_adapter import AdminApiAdapter
|
2026-02-01 17:28:00 +08:00
|
|
|
|
from src.api.base.context import ApiRequestContext
|
2026-03-14 11:59:07 +08:00
|
|
|
|
from src.api.base.pipeline import get_pipeline
|
2025-12-10 20:52:44 +08:00
|
|
|
|
from src.core.exceptions import NotFoundException
|
|
|
|
|
|
from src.database import get_db
|
2026-01-10 18:43:53 +08:00
|
|
|
|
from src.models.database import ProviderAPIKey
|
|
|
|
|
|
from src.models.endpoint_models import KeyRpmStatusResponse
|
2025-12-10 20:52:44 +08:00
|
|
|
|
from src.services.rate_limit.concurrency_manager import get_concurrency_manager
|
|
|
|
|
|
|
2026-01-10 18:43:53 +08:00
|
|
|
|
router = APIRouter(tags=["RPM Control"])
|
2026-03-14 11:59:07 +08:00
|
|
|
|
pipeline = get_pipeline()
|
2025-12-10 20:52:44 +08:00
|
|
|
|
|
|
|
|
|
|
|
2026-01-10 18:43:53 +08:00
|
|
|
|
@router.get("/rpm/key/{key_id}", response_model=KeyRpmStatusResponse)
|
|
|
|
|
|
async def get_key_rpm(
|
2025-12-10 20:52:44 +08:00
|
|
|
|
key_id: str,
|
|
|
|
|
|
request: Request,
|
|
|
|
|
|
db: Session = Depends(get_db),
|
2026-01-10 18:43:53 +08:00
|
|
|
|
) -> KeyRpmStatusResponse:
|
2026-01-07 14:55:07 +08:00
|
|
|
|
"""
|
2026-01-10 18:43:53 +08:00
|
|
|
|
获取 Key 当前 RPM 状态
|
2026-01-07 14:55:07 +08:00
|
|
|
|
|
2026-01-10 18:43:53 +08:00
|
|
|
|
查询指定 API Key 的实时 RPM 使用情况,包括当前 RPM 计数和最大 RPM 限制。
|
2026-01-07 14:55:07 +08:00
|
|
|
|
|
|
|
|
|
|
**路径参数**:
|
|
|
|
|
|
- `key_id`: API Key ID
|
|
|
|
|
|
|
|
|
|
|
|
**返回字段**:
|
|
|
|
|
|
- `key_id`: API Key ID
|
2026-01-10 18:43:53 +08:00
|
|
|
|
- `current_rpm`: 当前 RPM 计数
|
|
|
|
|
|
- `rpm_limit`: RPM 限制
|
2026-01-07 14:55:07 +08:00
|
|
|
|
"""
|
2026-01-10 18:43:53 +08:00
|
|
|
|
adapter = AdminKeyRpmAdapter(key_id=key_id)
|
2025-12-10 20:52:44 +08:00
|
|
|
|
return await pipeline.run(adapter=adapter, http_request=request, db=db, mode=adapter.mode)
|
|
|
|
|
|
|
|
|
|
|
|
|
2026-01-10 18:43:53 +08:00
|
|
|
|
@router.delete("/rpm/key/{key_id}")
|
|
|
|
|
|
async def reset_key_rpm(
|
|
|
|
|
|
key_id: str,
|
2025-12-10 20:52:44 +08:00
|
|
|
|
http_request: Request,
|
|
|
|
|
|
db: Session = Depends(get_db),
|
|
|
|
|
|
) -> dict:
|
2026-01-07 14:55:07 +08:00
|
|
|
|
"""
|
2026-01-10 18:43:53 +08:00
|
|
|
|
重置 Key RPM 计数器
|
2026-01-07 14:55:07 +08:00
|
|
|
|
|
2026-01-10 18:43:53 +08:00
|
|
|
|
重置指定 API Key 的 RPM 计数器,用于解决计数不准确的问题。
|
2026-01-07 14:55:07 +08:00
|
|
|
|
管理员功能,请谨慎使用。
|
|
|
|
|
|
|
2026-01-10 18:43:53 +08:00
|
|
|
|
**路径参数**:
|
|
|
|
|
|
- `key_id`: API Key ID
|
2026-01-07 14:55:07 +08:00
|
|
|
|
|
|
|
|
|
|
**返回字段**:
|
|
|
|
|
|
- `message`: 操作结果消息
|
|
|
|
|
|
"""
|
2026-01-10 18:43:53 +08:00
|
|
|
|
adapter = AdminResetKeyRpmAdapter(key_id=key_id)
|
2025-12-10 20:52:44 +08:00
|
|
|
|
return await pipeline.run(adapter=adapter, http_request=http_request, db=db, mode=adapter.mode)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# -------- Adapters --------
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@dataclass
|
2026-01-10 18:43:53 +08:00
|
|
|
|
class AdminKeyRpmAdapter(AdminApiAdapter):
|
2025-12-10 20:52:44 +08:00
|
|
|
|
key_id: str
|
|
|
|
|
|
|
2026-01-30 14:30:57 +08:00
|
|
|
|
async def handle(self, context: ApiRequestContext) -> Any: # type: ignore[override]
|
2025-12-10 20:52:44 +08:00
|
|
|
|
db = context.db
|
|
|
|
|
|
key = db.query(ProviderAPIKey).filter(ProviderAPIKey.id == self.key_id).first()
|
|
|
|
|
|
if not key:
|
|
|
|
|
|
raise NotFoundException(f"Key {self.key_id} 不存在")
|
|
|
|
|
|
|
|
|
|
|
|
concurrency_manager = await get_concurrency_manager()
|
2026-01-10 18:43:53 +08:00
|
|
|
|
key_count = await concurrency_manager.get_key_rpm_count(key_id=self.key_id)
|
2025-12-10 20:52:44 +08:00
|
|
|
|
|
2026-01-10 18:43:53 +08:00
|
|
|
|
return KeyRpmStatusResponse(
|
2025-12-10 20:52:44 +08:00
|
|
|
|
key_id=self.key_id,
|
2026-01-10 18:43:53 +08:00
|
|
|
|
current_rpm=key_count,
|
|
|
|
|
|
rpm_limit=key.rpm_limit,
|
2025-12-10 20:52:44 +08:00
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@dataclass
|
2026-01-10 18:43:53 +08:00
|
|
|
|
class AdminResetKeyRpmAdapter(AdminApiAdapter):
|
|
|
|
|
|
key_id: str
|
2025-12-10 20:52:44 +08:00
|
|
|
|
|
2026-01-30 14:30:57 +08:00
|
|
|
|
async def handle(self, context: ApiRequestContext) -> Any: # type: ignore[override]
|
2025-12-10 20:52:44 +08:00
|
|
|
|
concurrency_manager = await get_concurrency_manager()
|
2026-01-10 18:43:53 +08:00
|
|
|
|
await concurrency_manager.reset_key_rpm(key_id=self.key_id)
|
|
|
|
|
|
return {"message": "RPM 计数已重置"}
|