mirror of
https://github.com/fawney19/Aether.git
synced 2026-01-13 13:07:22 +08:00
feat: 添加访问令牌管理功能并升级至 0.2.4
- 新增 Management Token(访问令牌)功能,支持创建、更新、删除和管理 - 前端添加访问令牌管理页面,支持普通用户和管理员 - 后端实现完整的令牌生命周期管理 API - 添加数据库迁移脚本创建 management_tokens 表 - Nginx 配置添加 gzip 压缩,优化响应传输 - Dialog 组件添加 persistent 属性,防止意外关闭 - 为管理后台 API 添加详细的中文文档注释 - 简化多处类型注解,统一代码风格
This commit is contained in:
@@ -6,6 +6,7 @@ from .adaptive import router as adaptive_router
|
||||
from .api_keys import router as api_keys_router
|
||||
from .endpoints import router as endpoints_router
|
||||
from .ldap import router as ldap_router
|
||||
from .management_tokens import router as management_tokens_router
|
||||
from .models import router as models_router
|
||||
from .monitoring import router as monitoring_router
|
||||
from .provider_query import router as provider_query_router
|
||||
@@ -30,5 +31,6 @@ router.include_router(models_router)
|
||||
router.include_router(security_router)
|
||||
router.include_router(provider_query_router)
|
||||
router.include_router(ldap_router)
|
||||
router.include_router(management_tokens_router)
|
||||
|
||||
__all__ = ["router"]
|
||||
|
||||
@@ -73,7 +73,26 @@ async def list_standalone_api_keys(
|
||||
is_active: Optional[bool] = None,
|
||||
db: Session = Depends(get_db),
|
||||
):
|
||||
"""列出所有独立余额API Keys"""
|
||||
"""
|
||||
列出所有独立余额 API Keys
|
||||
|
||||
获取系统中所有独立余额 API Key 的列表。独立余额 Key 不关联用户配额,
|
||||
有独立的余额限制,主要用于给非注册用户使用。
|
||||
|
||||
**查询参数**:
|
||||
- `skip`: 跳过的记录数(分页偏移量),默认 0
|
||||
- `limit`: 返回的记录数(分页限制),默认 100,最大 500
|
||||
- `is_active`: 可选,根据启用状态筛选(true/false)
|
||||
|
||||
**返回字段**:
|
||||
- `api_keys`: API Key 列表,包含 id, name, key_display, is_active, current_balance_usd,
|
||||
balance_used_usd, total_requests, total_cost_usd, rate_limit, allowed_providers,
|
||||
allowed_api_formats, allowed_models, last_used_at, expires_at, created_at, updated_at,
|
||||
auto_delete_on_expiry 等字段
|
||||
- `total`: 符合条件的总记录数
|
||||
- `limit`: 当前分页限制
|
||||
- `skip`: 当前分页偏移量
|
||||
"""
|
||||
adapter = AdminListStandaloneKeysAdapter(skip=skip, limit=limit, is_active=is_active)
|
||||
return await pipeline.run(adapter=adapter, http_request=request, db=db, mode=adapter.mode)
|
||||
|
||||
@@ -84,7 +103,35 @@ async def create_standalone_api_key(
|
||||
key_data: CreateApiKeyRequest,
|
||||
db: Session = Depends(get_db),
|
||||
):
|
||||
"""创建独立余额API Key(必须设置余额限制)"""
|
||||
"""
|
||||
创建独立余额 API Key
|
||||
|
||||
创建一个新的独立余额 API Key。独立余额 Key 必须设置初始余额限制。
|
||||
|
||||
**请求体字段**:
|
||||
- `name`: API Key 的名称
|
||||
- `initial_balance_usd`: 必需,初始余额(美元),必须大于 0
|
||||
- `allowed_providers`: 可选,允许使用的提供商列表
|
||||
- `allowed_api_formats`: 可选,允许使用的 API 格式列表
|
||||
- `allowed_models`: 可选,允许使用的模型列表
|
||||
- `rate_limit`: 可选,速率限制配置(请求数/秒)
|
||||
- `expire_days`: 可选,过期天数(兼容旧版)
|
||||
- `expires_at`: 可选,过期时间(ISO 格式或 YYYY-MM-DD 格式,优先级高于 expire_days)
|
||||
- `auto_delete_on_expiry`: 可选,过期后是否自动删除
|
||||
|
||||
**返回字段**:
|
||||
- `id`: API Key ID
|
||||
- `key`: 完整的 API Key(仅在创建时返回一次)
|
||||
- `name`: API Key 名称
|
||||
- `key_display`: 脱敏显示的 Key
|
||||
- `is_standalone`: 是否为独立余额 Key(始终为 true)
|
||||
- `current_balance_usd`: 当前余额
|
||||
- `balance_used_usd`: 已使用余额
|
||||
- `rate_limit`: 速率限制配置
|
||||
- `expires_at`: 过期时间
|
||||
- `created_at`: 创建时间
|
||||
- `message`: 提示信息
|
||||
"""
|
||||
adapter = AdminCreateStandaloneKeyAdapter(key_data=key_data)
|
||||
return await pipeline.run(adapter=adapter, http_request=request, db=db, mode=adapter.mode)
|
||||
|
||||
@@ -93,20 +140,72 @@ async def create_standalone_api_key(
|
||||
async def update_api_key(
|
||||
key_id: str, request: Request, key_data: CreateApiKeyRequest, db: Session = Depends(get_db)
|
||||
):
|
||||
"""更新独立余额Key(可修改名称、过期时间、余额限制等)"""
|
||||
"""
|
||||
更新独立余额 API Key
|
||||
|
||||
更新指定 ID 的独立余额 API Key 的配置信息。
|
||||
|
||||
**路径参数**:
|
||||
- `key_id`: API Key ID
|
||||
|
||||
**请求体字段**:
|
||||
- `name`: 可选,API Key 的名称
|
||||
- `rate_limit`: 可选,速率限制配置(null 表示无限制)
|
||||
- `allowed_providers`: 可选,允许使用的提供商列表
|
||||
- `allowed_api_formats`: 可选,允许使用的 API 格式列表
|
||||
- `allowed_models`: 可选,允许使用的模型列表
|
||||
- `expire_days`: 可选,过期天数(兼容旧版)
|
||||
- `expires_at`: 可选,过期时间(ISO 格式或 YYYY-MM-DD 格式,优先级高于 expire_days,null 或空字符串表示永不过期)
|
||||
- `auto_delete_on_expiry`: 可选,过期后是否自动删除
|
||||
|
||||
**返回字段**:
|
||||
- `id`: API Key ID
|
||||
- `name`: API Key 名称
|
||||
- `key_display`: 脱敏显示的 Key
|
||||
- `is_active`: 是否启用
|
||||
- `current_balance_usd`: 当前余额
|
||||
- `balance_used_usd`: 已使用余额
|
||||
- `rate_limit`: 速率限制配置
|
||||
- `expires_at`: 过期时间
|
||||
- `updated_at`: 更新时间
|
||||
- `message`: 提示信息
|
||||
"""
|
||||
adapter = AdminUpdateApiKeyAdapter(key_id=key_id, key_data=key_data)
|
||||
return await pipeline.run(adapter=adapter, http_request=request, db=db, mode=adapter.mode)
|
||||
|
||||
|
||||
@router.patch("/{key_id}")
|
||||
async def toggle_api_key(key_id: str, request: Request, db: Session = Depends(get_db)):
|
||||
"""Toggle API key active status (PATCH with is_active in body)"""
|
||||
"""
|
||||
切换 API Key 启用状态
|
||||
|
||||
切换指定 API Key 的启用/禁用状态。
|
||||
|
||||
**路径参数**:
|
||||
- `key_id`: API Key ID
|
||||
|
||||
**返回字段**:
|
||||
- `id`: API Key ID
|
||||
- `is_active`: 新的启用状态
|
||||
- `message`: 提示信息
|
||||
"""
|
||||
adapter = AdminToggleApiKeyAdapter(key_id=key_id)
|
||||
return await pipeline.run(adapter=adapter, http_request=request, db=db, mode=adapter.mode)
|
||||
|
||||
|
||||
@router.delete("/{key_id}")
|
||||
async def delete_api_key(key_id: str, request: Request, db: Session = Depends(get_db)):
|
||||
"""
|
||||
删除 API Key
|
||||
|
||||
删除指定的 API Key。此操作不可逆。
|
||||
|
||||
**路径参数**:
|
||||
- `key_id`: API Key ID
|
||||
|
||||
**返回字段**:
|
||||
- `message`: 提示信息
|
||||
"""
|
||||
adapter = AdminDeleteApiKeyAdapter(key_id=key_id)
|
||||
return await pipeline.run(adapter=adapter, http_request=request, db=db, mode=adapter.mode)
|
||||
|
||||
@@ -117,7 +216,24 @@ async def add_balance_to_key(
|
||||
request: Request,
|
||||
db: Session = Depends(get_db),
|
||||
):
|
||||
"""Adjust balance for standalone API key (positive to add, negative to deduct)"""
|
||||
"""
|
||||
调整独立余额 API Key 的余额
|
||||
|
||||
为指定的独立余额 API Key 增加或扣除余额。
|
||||
|
||||
**路径参数**:
|
||||
- `key_id`: API Key ID
|
||||
|
||||
**请求体字段**:
|
||||
- `amount_usd`: 调整金额(美元),正数为充值,负数为扣除
|
||||
|
||||
**返回字段**:
|
||||
- `id`: API Key ID
|
||||
- `name`: API Key 名称
|
||||
- `current_balance_usd`: 调整后的当前余额
|
||||
- `balance_used_usd`: 已使用余额
|
||||
- `message`: 提示信息
|
||||
"""
|
||||
# 从请求体获取调整金额
|
||||
body = await request.json()
|
||||
amount_usd = body.get("amount_usd")
|
||||
@@ -162,7 +278,24 @@ async def get_api_key_detail(
|
||||
include_key: bool = Query(False, description="Include full decrypted key in response"),
|
||||
db: Session = Depends(get_db),
|
||||
):
|
||||
"""Get API key detail, optionally include full key"""
|
||||
"""
|
||||
获取 API Key 详情
|
||||
|
||||
获取指定 API Key 的详细信息。可选择是否返回完整的解密密钥。
|
||||
|
||||
**路径参数**:
|
||||
- `key_id`: API Key ID
|
||||
|
||||
**查询参数**:
|
||||
- `include_key`: 是否包含完整的解密密钥,默认 false
|
||||
|
||||
**返回字段**:
|
||||
- 当 include_key=false 时,返回基本信息:id, user_id, name, key_display, is_active,
|
||||
is_standalone, current_balance_usd, balance_used_usd, total_requests, total_cost_usd,
|
||||
rate_limit, allowed_providers, allowed_api_formats, allowed_models, last_used_at,
|
||||
expires_at, created_at, updated_at
|
||||
- 当 include_key=true 时,返回完整密钥:key
|
||||
"""
|
||||
if include_key:
|
||||
adapter = AdminGetFullKeyAdapter(key_id=key_id)
|
||||
else:
|
||||
|
||||
@@ -7,7 +7,7 @@ from .health import router as health_router
|
||||
from .keys import router as keys_router
|
||||
from .routes import router as routes_router
|
||||
|
||||
router = APIRouter(prefix="/api/admin/endpoints", tags=["Endpoint Management"])
|
||||
router = APIRouter(prefix="/api/admin/endpoints", tags=["Admin - Endpoints"])
|
||||
|
||||
# Endpoint CRUD
|
||||
router.include_router(routes_router)
|
||||
|
||||
@@ -29,7 +29,19 @@ async def get_endpoint_concurrency(
|
||||
request: Request,
|
||||
db: Session = Depends(get_db),
|
||||
) -> ConcurrencyStatusResponse:
|
||||
"""获取 Endpoint 当前并发状态"""
|
||||
"""
|
||||
获取 Endpoint 当前并发状态
|
||||
|
||||
查询指定 Endpoint 的实时并发使用情况,包括当前并发数和最大并发限制。
|
||||
|
||||
**路径参数**:
|
||||
- `endpoint_id`: Endpoint ID
|
||||
|
||||
**返回字段**:
|
||||
- `endpoint_id`: Endpoint ID
|
||||
- `endpoint_current_concurrency`: 当前并发数
|
||||
- `endpoint_max_concurrent`: 最大并发限制
|
||||
"""
|
||||
adapter = AdminEndpointConcurrencyAdapter(endpoint_id=endpoint_id)
|
||||
return await pipeline.run(adapter=adapter, http_request=request, db=db, mode=adapter.mode)
|
||||
|
||||
@@ -40,7 +52,19 @@ async def get_key_concurrency(
|
||||
request: Request,
|
||||
db: Session = Depends(get_db),
|
||||
) -> ConcurrencyStatusResponse:
|
||||
"""获取 Key 当前并发状态"""
|
||||
"""
|
||||
获取 Key 当前并发状态
|
||||
|
||||
查询指定 API Key 的实时并发使用情况,包括当前并发数和最大并发限制。
|
||||
|
||||
**路径参数**:
|
||||
- `key_id`: API Key ID
|
||||
|
||||
**返回字段**:
|
||||
- `key_id`: API Key ID
|
||||
- `key_current_concurrency`: 当前并发数
|
||||
- `key_max_concurrent`: 最大并发限制
|
||||
"""
|
||||
adapter = AdminKeyConcurrencyAdapter(key_id=key_id)
|
||||
return await pipeline.run(adapter=adapter, http_request=request, db=db, mode=adapter.mode)
|
||||
|
||||
@@ -51,7 +75,19 @@ async def reset_concurrency(
|
||||
http_request: Request,
|
||||
db: Session = Depends(get_db),
|
||||
) -> dict:
|
||||
"""Reset concurrency counters (admin function, use with caution)"""
|
||||
"""
|
||||
重置并发计数器
|
||||
|
||||
重置指定 Endpoint 或 Key 的并发计数器,用于解决计数不准确的问题。
|
||||
管理员功能,请谨慎使用。
|
||||
|
||||
**请求体字段**:
|
||||
- `endpoint_id`: Endpoint ID(可选)
|
||||
- `key_id`: API Key ID(可选)
|
||||
|
||||
**返回字段**:
|
||||
- `message`: 操作结果消息
|
||||
"""
|
||||
adapter = AdminResetConcurrencyAdapter(endpoint_id=request.endpoint_id, key_id=request.key_id)
|
||||
return await pipeline.run(adapter=adapter, http_request=http_request, db=db, mode=adapter.mode)
|
||||
|
||||
|
||||
@@ -36,7 +36,20 @@ async def get_health_summary(
|
||||
request: Request,
|
||||
db: Session = Depends(get_db),
|
||||
) -> HealthSummaryResponse:
|
||||
"""获取健康状态摘要"""
|
||||
"""
|
||||
获取健康状态摘要
|
||||
|
||||
获取系统整体健康状态摘要,包括所有 Provider、Endpoint 和 Key 的健康状态统计。
|
||||
|
||||
**返回字段**:
|
||||
- `total_providers`: Provider 总数
|
||||
- `active_providers`: 活跃 Provider 数量
|
||||
- `total_endpoints`: Endpoint 总数
|
||||
- `active_endpoints`: 活跃 Endpoint 数量
|
||||
- `total_keys`: Key 总数
|
||||
- `active_keys`: 活跃 Key 数量
|
||||
- `circuit_breaker_open_keys`: 熔断的 Key 数量
|
||||
"""
|
||||
adapter = AdminHealthSummaryAdapter()
|
||||
return await pipeline.run(adapter=adapter, http_request=request, db=db, mode=adapter.mode)
|
||||
|
||||
@@ -50,9 +63,21 @@ async def get_endpoint_health_status(
|
||||
"""
|
||||
获取端点健康状态(简化视图,与用户端点统一)
|
||||
|
||||
获取按 API 格式聚合的端点健康状态时间线,基于 Usage 表统计,
|
||||
返回 50 个时间段的聚合状态,适用于快速查看整体健康趋势。
|
||||
|
||||
与 /health/api-formats 的区别:
|
||||
- /health/status: 返回聚合的时间线状态(50个时间段),基于 Usage 表
|
||||
- /health/api-formats: 返回详细的事件列表,基于 RequestCandidate 表
|
||||
|
||||
**查询参数**:
|
||||
- `lookback_hours`: 回溯的小时数(1-72),默认 6
|
||||
|
||||
**返回字段**:
|
||||
- `api_format`: API 格式名称
|
||||
- `timeline`: 时间线数据(50个时间段)
|
||||
- `time_range_start`: 时间范围起始
|
||||
- `time_range_end`: 时间范围结束
|
||||
"""
|
||||
adapter = AdminEndpointHealthStatusAdapter(lookback_hours=lookback_hours)
|
||||
return await pipeline.run(adapter=adapter, http_request=request, db=db, mode=adapter.mode)
|
||||
@@ -65,7 +90,33 @@ async def get_api_format_health_monitor(
|
||||
per_format_limit: int = Query(60, ge=10, le=200, description="每个 API 格式的事件数量"),
|
||||
db: Session = Depends(get_db),
|
||||
) -> ApiFormatHealthMonitorResponse:
|
||||
"""获取按 API 格式聚合的健康监控时间线(详细事件列表)"""
|
||||
"""
|
||||
获取按 API 格式聚合的健康监控时间线(详细事件列表)
|
||||
|
||||
获取每个 API 格式的详细健康监控数据,包括请求事件列表、成功率统计、
|
||||
时间线数据等,基于 RequestCandidate 表查询,适用于详细分析。
|
||||
|
||||
**查询参数**:
|
||||
- `lookback_hours`: 回溯的小时数(1-72),默认 6
|
||||
- `per_format_limit`: 每个 API 格式返回的事件数量(10-200),默认 60
|
||||
|
||||
**返回字段**:
|
||||
- `generated_at`: 数据生成时间
|
||||
- `formats`: API 格式健康监控数据列表
|
||||
- `api_format`: API 格式名称
|
||||
- `total_attempts`: 总请求数
|
||||
- `success_count`: 成功请求数
|
||||
- `failed_count`: 失败请求数
|
||||
- `skipped_count`: 跳过请求数
|
||||
- `success_rate`: 成功率
|
||||
- `provider_count`: Provider 数量
|
||||
- `key_count`: Key 数量
|
||||
- `last_event_at`: 最后事件时间
|
||||
- `events`: 事件列表
|
||||
- `timeline`: 时间线数据
|
||||
- `time_range_start`: 时间范围起始
|
||||
- `time_range_end`: 时间范围结束
|
||||
"""
|
||||
adapter = AdminApiFormatHealthMonitorAdapter(
|
||||
lookback_hours=lookback_hours,
|
||||
per_format_limit=per_format_limit,
|
||||
@@ -79,7 +130,26 @@ async def get_key_health(
|
||||
request: Request,
|
||||
db: Session = Depends(get_db),
|
||||
) -> HealthStatusResponse:
|
||||
"""获取 Key 健康状态"""
|
||||
"""
|
||||
获取 Key 健康状态
|
||||
|
||||
获取指定 API Key 的健康状态详情,包括健康分数、连续失败次数、
|
||||
熔断器状态等信息。
|
||||
|
||||
**路径参数**:
|
||||
- `key_id`: API Key ID
|
||||
|
||||
**返回字段**:
|
||||
- `key_id`: API Key ID
|
||||
- `key_health_score`: 健康分数(0.0-1.0)
|
||||
- `key_consecutive_failures`: 连续失败次数
|
||||
- `key_last_failure_at`: 最后失败时间
|
||||
- `key_is_active`: 是否活跃
|
||||
- `key_statistics`: 统计信息
|
||||
- `circuit_breaker_open`: 熔断器是否打开
|
||||
- `circuit_breaker_open_at`: 熔断器打开时间
|
||||
- `next_probe_at`: 下次探测时间
|
||||
"""
|
||||
adapter = AdminKeyHealthAdapter(key_id=key_id)
|
||||
return await pipeline.run(adapter=adapter, http_request=request, db=db, mode=adapter.mode)
|
||||
|
||||
@@ -91,13 +161,20 @@ async def recover_key_health(
|
||||
db: Session = Depends(get_db),
|
||||
) -> dict:
|
||||
"""
|
||||
Recover key health status
|
||||
恢复 Key 健康状态
|
||||
|
||||
Resets health_score to 1.0, closes circuit breaker,
|
||||
cancels auto-disable, and resets all failure counts.
|
||||
手动恢复指定 Key 的健康状态,将健康分数重置为 1.0,关闭熔断器,
|
||||
取消自动禁用,并重置所有失败计数。
|
||||
|
||||
Parameters:
|
||||
- key_id: Key ID (path parameter)
|
||||
**路径参数**:
|
||||
- `key_id`: API Key ID
|
||||
|
||||
**返回字段**:
|
||||
- `message`: 操作结果消息
|
||||
- `details`: 详细信息
|
||||
- `health_score`: 健康分数
|
||||
- `circuit_breaker_open`: 熔断器状态
|
||||
- `is_active`: 是否活跃
|
||||
"""
|
||||
adapter = AdminRecoverKeyHealthAdapter(key_id=key_id)
|
||||
return await pipeline.run(adapter=adapter, http_request=request, db=db, mode=adapter.mode)
|
||||
@@ -109,12 +186,21 @@ async def recover_all_keys_health(
|
||||
db: Session = Depends(get_db),
|
||||
) -> dict:
|
||||
"""
|
||||
Batch recover all circuit-broken keys
|
||||
批量恢复所有熔断 Key 的健康状态
|
||||
|
||||
Finds all keys with circuit_breaker_open=True and:
|
||||
1. Resets health_score to 1.0
|
||||
2. Closes circuit breaker
|
||||
3. Resets failure counts
|
||||
查找所有处于熔断状态的 Key(circuit_breaker_open=True),
|
||||
并批量执行以下操作:
|
||||
1. 将健康分数重置为 1.0
|
||||
2. 关闭熔断器
|
||||
3. 重置失败计数
|
||||
|
||||
**返回字段**:
|
||||
- `message`: 操作结果消息
|
||||
- `recovered_count`: 恢复的 Key 数量
|
||||
- `recovered_keys`: 恢复的 Key 列表
|
||||
- `key_id`: Key ID
|
||||
- `key_name`: Key 名称
|
||||
- `endpoint_id`: Endpoint ID
|
||||
"""
|
||||
adapter = AdminRecoverAllKeysHealthAdapter()
|
||||
return await pipeline.run(adapter=adapter, http_request=request, db=db, mode=adapter.mode)
|
||||
|
||||
@@ -37,7 +37,33 @@ async def list_endpoint_keys(
|
||||
limit: int = Query(100, ge=1, le=1000, description="返回的最大记录数"),
|
||||
db: Session = Depends(get_db),
|
||||
) -> List[EndpointAPIKeyResponse]:
|
||||
"""获取 Endpoint 的所有 Keys"""
|
||||
"""
|
||||
获取 Endpoint 的所有 Keys
|
||||
|
||||
获取指定 Endpoint 下的所有 API Key 列表,包括 Key 的配置、统计信息等。
|
||||
结果按优先级和创建时间排序。
|
||||
|
||||
**路径参数**:
|
||||
- `endpoint_id`: Endpoint ID
|
||||
|
||||
**查询参数**:
|
||||
- `skip`: 跳过的记录数,用于分页(默认 0)
|
||||
- `limit`: 返回的最大记录数(1-1000,默认 100)
|
||||
|
||||
**返回字段**:
|
||||
- `id`: Key ID
|
||||
- `name`: Key 名称
|
||||
- `api_key_masked`: 脱敏后的 API Key
|
||||
- `internal_priority`: 内部优先级
|
||||
- `global_priority`: 全局优先级
|
||||
- `rate_multiplier`: 速率倍数
|
||||
- `max_concurrent`: 最大并发数(null 表示自适应模式)
|
||||
- `is_adaptive`: 是否为自适应并发模式
|
||||
- `effective_limit`: 有效并发限制
|
||||
- `success_rate`: 成功率
|
||||
- `avg_response_time_ms`: 平均响应时间(毫秒)
|
||||
- 其他配置和统计字段
|
||||
"""
|
||||
adapter = AdminListEndpointKeysAdapter(
|
||||
endpoint_id=endpoint_id,
|
||||
skip=skip,
|
||||
@@ -53,7 +79,32 @@ async def add_endpoint_key(
|
||||
request: Request,
|
||||
db: Session = Depends(get_db),
|
||||
) -> EndpointAPIKeyResponse:
|
||||
"""为 Endpoint 添加 Key"""
|
||||
"""
|
||||
为 Endpoint 添加 Key
|
||||
|
||||
为指定 Endpoint 添加新的 API Key,支持配置并发限制、速率倍数、
|
||||
优先级、配额限制、能力限制等。
|
||||
|
||||
**路径参数**:
|
||||
- `endpoint_id`: Endpoint ID
|
||||
|
||||
**请求体字段**:
|
||||
- `endpoint_id`: Endpoint ID(必须与路径参数一致)
|
||||
- `api_key`: API Key 原文(将被加密存储)
|
||||
- `name`: Key 名称
|
||||
- `note`: 备注(可选)
|
||||
- `rate_multiplier`: 速率倍数(默认 1.0)
|
||||
- `internal_priority`: 内部优先级(默认 100)
|
||||
- `max_concurrent`: 最大并发数(null 表示自适应模式)
|
||||
- `rate_limit`: 每分钟请求限制(可选)
|
||||
- `daily_limit`: 每日请求限制(可选)
|
||||
- `monthly_limit`: 每月请求限制(可选)
|
||||
- `allowed_models`: 允许的模型列表(可选)
|
||||
- `capabilities`: 能力配置(可选)
|
||||
|
||||
**返回字段**:
|
||||
- 包含完整的 Key 信息,其中 `api_key_plain` 为原文(仅在创建时返回)
|
||||
"""
|
||||
adapter = AdminCreateEndpointKeyAdapter(endpoint_id=endpoint_id, key_data=key_data)
|
||||
return await pipeline.run(adapter=adapter, http_request=request, db=db, mode=adapter.mode)
|
||||
|
||||
@@ -65,7 +116,32 @@ async def update_endpoint_key(
|
||||
request: Request,
|
||||
db: Session = Depends(get_db),
|
||||
) -> EndpointAPIKeyResponse:
|
||||
"""更新 Endpoint Key"""
|
||||
"""
|
||||
更新 Endpoint Key
|
||||
|
||||
更新指定 Key 的配置,支持修改并发限制、速率倍数、优先级、
|
||||
配额限制、能力限制等。支持部分更新。
|
||||
|
||||
**路径参数**:
|
||||
- `key_id`: Key ID
|
||||
|
||||
**请求体字段**(均为可选):
|
||||
- `api_key`: 新的 API Key 原文
|
||||
- `name`: Key 名称
|
||||
- `note`: 备注
|
||||
- `rate_multiplier`: 速率倍数
|
||||
- `internal_priority`: 内部优先级
|
||||
- `max_concurrent`: 最大并发数(设置为 null 可切换到自适应模式)
|
||||
- `rate_limit`: 每分钟请求限制
|
||||
- `daily_limit`: 每日请求限制
|
||||
- `monthly_limit`: 每月请求限制
|
||||
- `allowed_models`: 允许的模型列表
|
||||
- `capabilities`: 能力配置
|
||||
- `is_active`: 是否活跃
|
||||
|
||||
**返回字段**:
|
||||
- 包含更新后的完整 Key 信息
|
||||
"""
|
||||
adapter = AdminUpdateEndpointKeyAdapter(key_id=key_id, key_data=key_data)
|
||||
return await pipeline.run(adapter=adapter, http_request=request, db=db, mode=adapter.mode)
|
||||
|
||||
@@ -75,7 +151,31 @@ async def get_keys_grouped_by_format(
|
||||
request: Request,
|
||||
db: Session = Depends(get_db),
|
||||
) -> dict:
|
||||
"""获取按 API 格式分组的所有 Keys(用于全局优先级管理)"""
|
||||
"""
|
||||
获取按 API 格式分组的所有 Keys
|
||||
|
||||
获取所有活跃的 Key,按 API 格式分组返回,用于全局优先级管理。
|
||||
每个 Key 包含基本信息、健康度指标、能力标签等。
|
||||
|
||||
**返回字段**:
|
||||
- 返回一个字典,键为 API 格式,值为该格式下的 Key 列表
|
||||
- 每个 Key 包含:
|
||||
- `id`: Key ID
|
||||
- `name`: Key 名称
|
||||
- `api_key_masked`: 脱敏后的 API Key
|
||||
- `internal_priority`: 内部优先级
|
||||
- `global_priority`: 全局优先级
|
||||
- `rate_multiplier`: 速率倍数
|
||||
- `is_active`: 是否活跃
|
||||
- `circuit_breaker_open`: 熔断器状态
|
||||
- `provider_name`: Provider 名称
|
||||
- `endpoint_base_url`: Endpoint 基础 URL
|
||||
- `api_format`: API 格式
|
||||
- `capabilities`: 能力简称列表
|
||||
- `success_rate`: 成功率
|
||||
- `avg_response_time_ms`: 平均响应时间
|
||||
- `request_count`: 请求总数
|
||||
"""
|
||||
adapter = AdminGetKeysGroupedByFormatAdapter()
|
||||
return await pipeline.run(adapter=adapter, http_request=request, db=db, mode=adapter.mode)
|
||||
|
||||
@@ -86,7 +186,18 @@ async def reveal_endpoint_key(
|
||||
request: Request,
|
||||
db: Session = Depends(get_db),
|
||||
) -> dict:
|
||||
"""获取完整的 API Key(用于查看和复制)"""
|
||||
"""
|
||||
获取完整的 API Key
|
||||
|
||||
解密并返回指定 Key 的完整原文,用于查看和复制。
|
||||
此操作会被记录到审计日志。
|
||||
|
||||
**路径参数**:
|
||||
- `key_id`: Key ID
|
||||
|
||||
**返回字段**:
|
||||
- `api_key`: 完整的 API Key 原文
|
||||
"""
|
||||
adapter = AdminRevealEndpointKeyAdapter(key_id=key_id)
|
||||
return await pipeline.run(adapter=adapter, http_request=request, db=db, mode=adapter.mode)
|
||||
|
||||
@@ -97,7 +208,17 @@ async def delete_endpoint_key(
|
||||
request: Request,
|
||||
db: Session = Depends(get_db),
|
||||
) -> dict:
|
||||
"""删除 Endpoint Key"""
|
||||
"""
|
||||
删除 Endpoint Key
|
||||
|
||||
删除指定的 API Key。此操作不可逆,请谨慎使用。
|
||||
|
||||
**路径参数**:
|
||||
- `key_id`: Key ID
|
||||
|
||||
**返回字段**:
|
||||
- `message`: 操作结果消息
|
||||
"""
|
||||
adapter = AdminDeleteEndpointKeyAdapter(key_id=key_id)
|
||||
return await pipeline.run(adapter=adapter, http_request=request, db=db, mode=adapter.mode)
|
||||
|
||||
@@ -109,7 +230,24 @@ async def batch_update_key_priority(
|
||||
priority_data: BatchUpdateKeyPriorityRequest,
|
||||
db: Session = Depends(get_db),
|
||||
) -> dict:
|
||||
"""批量更新 Endpoint 下 Keys 的优先级(用于拖动排序)"""
|
||||
"""
|
||||
批量更新 Endpoint 下 Keys 的优先级
|
||||
|
||||
批量更新指定 Endpoint 下多个 Key 的内部优先级,用于拖动排序。
|
||||
所有 Key 必须属于指定的 Endpoint。
|
||||
|
||||
**路径参数**:
|
||||
- `endpoint_id`: Endpoint ID
|
||||
|
||||
**请求体字段**:
|
||||
- `priorities`: 优先级列表
|
||||
- `key_id`: Key ID
|
||||
- `internal_priority`: 新的内部优先级
|
||||
|
||||
**返回字段**:
|
||||
- `message`: 操作结果消息
|
||||
- `updated_count`: 实际更新的 Key 数量
|
||||
"""
|
||||
adapter = AdminBatchUpdateKeyPriorityAdapter(endpoint_id=endpoint_id, priority_data=priority_data)
|
||||
return await pipeline.run(adapter=adapter, http_request=request, db=db, mode=adapter.mode)
|
||||
|
||||
|
||||
@@ -45,7 +45,36 @@ async def list_provider_endpoints(
|
||||
limit: int = Query(100, ge=1, le=1000, description="返回的最大记录数"),
|
||||
db: Session = Depends(get_db),
|
||||
) -> List[ProviderEndpointResponse]:
|
||||
"""获取指定 Provider 的所有 Endpoints"""
|
||||
"""
|
||||
获取指定 Provider 的所有 Endpoints
|
||||
|
||||
获取指定 Provider 下的所有 Endpoint 列表,包括配置、统计信息等。
|
||||
结果按创建时间倒序排列。
|
||||
|
||||
**路径参数**:
|
||||
- `provider_id`: Provider ID
|
||||
|
||||
**查询参数**:
|
||||
- `skip`: 跳过的记录数,用于分页(默认 0)
|
||||
- `limit`: 返回的最大记录数(1-1000,默认 100)
|
||||
|
||||
**返回字段**:
|
||||
- `id`: Endpoint ID
|
||||
- `provider_id`: Provider ID
|
||||
- `provider_name`: Provider 名称
|
||||
- `api_format`: API 格式
|
||||
- `base_url`: 基础 URL
|
||||
- `custom_path`: 自定义路径
|
||||
- `timeout`: 超时时间(秒)
|
||||
- `max_retries`: 最大重试次数
|
||||
- `max_concurrent`: 最大并发数
|
||||
- `rate_limit`: 速率限制
|
||||
- `is_active`: 是否活跃
|
||||
- `total_keys`: Key 总数
|
||||
- `active_keys`: 活跃 Key 数量
|
||||
- `proxy`: 代理配置(密码已脱敏)
|
||||
- 其他配置字段
|
||||
"""
|
||||
adapter = AdminListProviderEndpointsAdapter(
|
||||
provider_id=provider_id,
|
||||
skip=skip,
|
||||
@@ -61,7 +90,31 @@ async def create_provider_endpoint(
|
||||
request: Request,
|
||||
db: Session = Depends(get_db),
|
||||
) -> ProviderEndpointResponse:
|
||||
"""为 Provider 创建新的 Endpoint"""
|
||||
"""
|
||||
为 Provider 创建新的 Endpoint
|
||||
|
||||
为指定 Provider 创建新的 Endpoint,每个 Provider 的每种 API 格式
|
||||
只能创建一个 Endpoint。
|
||||
|
||||
**路径参数**:
|
||||
- `provider_id`: Provider ID
|
||||
|
||||
**请求体字段**:
|
||||
- `provider_id`: Provider ID(必须与路径参数一致)
|
||||
- `api_format`: API 格式(如 claude、openai、gemini 等)
|
||||
- `base_url`: 基础 URL
|
||||
- `custom_path`: 自定义路径(可选)
|
||||
- `headers`: 自定义请求头(可选)
|
||||
- `timeout`: 超时时间(秒,默认 300)
|
||||
- `max_retries`: 最大重试次数(默认 2)
|
||||
- `max_concurrent`: 最大并发数(可选)
|
||||
- `rate_limit`: 速率限制(可选)
|
||||
- `config`: 额外配置(可选)
|
||||
- `proxy`: 代理配置(可选)
|
||||
|
||||
**返回字段**:
|
||||
- 包含完整的 Endpoint 信息
|
||||
"""
|
||||
adapter = AdminCreateProviderEndpointAdapter(
|
||||
provider_id=provider_id,
|
||||
endpoint_data=endpoint_data,
|
||||
@@ -75,7 +128,31 @@ async def get_endpoint(
|
||||
request: Request,
|
||||
db: Session = Depends(get_db),
|
||||
) -> ProviderEndpointResponse:
|
||||
"""获取 Endpoint 详情"""
|
||||
"""
|
||||
获取 Endpoint 详情
|
||||
|
||||
获取指定 Endpoint 的详细信息,包括配置、统计信息等。
|
||||
|
||||
**路径参数**:
|
||||
- `endpoint_id`: Endpoint ID
|
||||
|
||||
**返回字段**:
|
||||
- `id`: Endpoint ID
|
||||
- `provider_id`: Provider ID
|
||||
- `provider_name`: Provider 名称
|
||||
- `api_format`: API 格式
|
||||
- `base_url`: 基础 URL
|
||||
- `custom_path`: 自定义路径
|
||||
- `timeout`: 超时时间(秒)
|
||||
- `max_retries`: 最大重试次数
|
||||
- `max_concurrent`: 最大并发数
|
||||
- `rate_limit`: 速率限制
|
||||
- `is_active`: 是否活跃
|
||||
- `total_keys`: Key 总数
|
||||
- `active_keys`: 活跃 Key 数量
|
||||
- `proxy`: 代理配置(密码已脱敏)
|
||||
- 其他配置字段
|
||||
"""
|
||||
adapter = AdminGetProviderEndpointAdapter(endpoint_id=endpoint_id)
|
||||
return await pipeline.run(adapter=adapter, http_request=request, db=db, mode=adapter.mode)
|
||||
|
||||
@@ -87,7 +164,29 @@ async def update_endpoint(
|
||||
request: Request,
|
||||
db: Session = Depends(get_db),
|
||||
) -> ProviderEndpointResponse:
|
||||
"""更新 Endpoint"""
|
||||
"""
|
||||
更新 Endpoint
|
||||
|
||||
更新指定 Endpoint 的配置。支持部分更新。
|
||||
|
||||
**路径参数**:
|
||||
- `endpoint_id`: Endpoint ID
|
||||
|
||||
**请求体字段**(均为可选):
|
||||
- `base_url`: 基础 URL
|
||||
- `custom_path`: 自定义路径
|
||||
- `headers`: 自定义请求头
|
||||
- `timeout`: 超时时间(秒)
|
||||
- `max_retries`: 最大重试次数
|
||||
- `max_concurrent`: 最大并发数
|
||||
- `rate_limit`: 速率限制
|
||||
- `is_active`: 是否活跃
|
||||
- `config`: 额外配置
|
||||
- `proxy`: 代理配置(设置为 null 可清除代理)
|
||||
|
||||
**返回字段**:
|
||||
- 包含更新后的完整 Endpoint 信息
|
||||
"""
|
||||
adapter = AdminUpdateProviderEndpointAdapter(
|
||||
endpoint_id=endpoint_id,
|
||||
endpoint_data=endpoint_data,
|
||||
@@ -101,7 +200,19 @@ async def delete_endpoint(
|
||||
request: Request,
|
||||
db: Session = Depends(get_db),
|
||||
) -> dict:
|
||||
"""删除 Endpoint(级联删除所有关联的 Keys)"""
|
||||
"""
|
||||
删除 Endpoint
|
||||
|
||||
删除指定的 Endpoint,同时级联删除所有关联的 API Keys。
|
||||
此操作不可逆,请谨慎使用。
|
||||
|
||||
**路径参数**:
|
||||
- `endpoint_id`: Endpoint ID
|
||||
|
||||
**返回字段**:
|
||||
- `message`: 操作结果消息
|
||||
- `deleted_keys_count`: 同时删除的 Key 数量
|
||||
"""
|
||||
adapter = AdminDeleteProviderEndpointAdapter(endpoint_id=endpoint_id)
|
||||
return await pipeline.run(adapter=adapter, http_request=request, db=db, mode=adapter.mode)
|
||||
|
||||
|
||||
@@ -166,21 +166,95 @@ class LDAPConfigTest(BaseModel):
|
||||
|
||||
@router.get("/config")
|
||||
async def get_ldap_config(request: Request, db: Session = Depends(get_db)) -> Any:
|
||||
"""获取LDAP配置(管理员)"""
|
||||
"""
|
||||
获取 LDAP 配置
|
||||
|
||||
获取系统当前的 LDAP 认证配置信息,用于管理界面显示和编辑。
|
||||
密码字段不会返回原文,仅返回是否已设置的标志。
|
||||
|
||||
**返回字段**:
|
||||
- `server_url`: LDAP 服务器地址(如:ldap://ldap.example.com:389)
|
||||
- `bind_dn`: 绑定 DN(如:cn=admin,dc=example,dc=com)
|
||||
- `base_dn`: 搜索基准 DN(如:ou=users,dc=example,dc=com)
|
||||
- `has_bind_password`: 是否已设置绑定密码(布尔值)
|
||||
- `user_search_filter`: 用户搜索过滤器(默认:(uid={username}))
|
||||
- `username_attr`: 用户名属性(默认:uid)
|
||||
- `email_attr`: 邮箱属性(默认:mail)
|
||||
- `display_name_attr`: 显示名称属性(默认:cn)
|
||||
- `is_enabled`: 是否启用 LDAP 认证
|
||||
- `is_exclusive`: 是否仅允许 LDAP 登录(独占模式)
|
||||
- `use_starttls`: 是否使用 STARTTLS 加密连接
|
||||
- `connect_timeout`: 连接超时时间(秒,1-60)
|
||||
"""
|
||||
adapter = AdminGetLDAPConfigAdapter()
|
||||
return await pipeline.run(adapter=adapter, http_request=request, db=db, mode=adapter.mode)
|
||||
|
||||
|
||||
@router.put("/config")
|
||||
async def update_ldap_config(request: Request, db: Session = Depends(get_db)) -> Any:
|
||||
"""更新LDAP配置(管理员)"""
|
||||
"""
|
||||
更新 LDAP 配置
|
||||
|
||||
更新系统的 LDAP 认证配置。支持完整配置更新,包括连接参数、
|
||||
搜索过滤器、属性映射等。提供多重安全校验,防止误锁定管理员。
|
||||
|
||||
**请求体字段**:
|
||||
- `server_url`: LDAP 服务器地址(必填,1-255字符)
|
||||
- `bind_dn`: 绑定 DN(必填,1-255字符)
|
||||
- `bind_password`: 绑定密码(可选,设为空字符串可清除密码)
|
||||
- `base_dn`: 搜索基准 DN(必填,1-255字符)
|
||||
- `user_search_filter`: 用户搜索过滤器(必须包含 {username} 占位符,默认:(uid={username}))
|
||||
- `username_attr`: 用户名属性(默认:uid)
|
||||
- `email_attr`: 邮箱属性(默认:mail)
|
||||
- `display_name_attr`: 显示名称属性(默认:cn)
|
||||
- `is_enabled`: 是否启用 LDAP 认证
|
||||
- `is_exclusive`: 是否仅允许 LDAP 登录(需先启用 LDAP)
|
||||
- `use_starttls`: 是否使用 STARTTLS 加密连接
|
||||
- `connect_timeout`: 连接超时时间(秒,1-60,默认 10)
|
||||
|
||||
**安全校验**:
|
||||
- 启用 LDAP 时必须设置有效的绑定密码
|
||||
- 启用独占模式前会检查是否有至少 1 个有效的本地管理员账户
|
||||
- 独占模式要求先启用 LDAP 认证
|
||||
- 搜索过滤器必须包含 {username} 占位符且括号匹配
|
||||
- 搜索过滤器嵌套层数不超过 5 层,长度不超过 200 字符
|
||||
|
||||
**返回字段**:
|
||||
- `message`: 操作结果消息
|
||||
"""
|
||||
adapter = AdminUpdateLDAPConfigAdapter()
|
||||
return await pipeline.run(adapter=adapter, http_request=request, db=db, mode=adapter.mode)
|
||||
|
||||
|
||||
@router.post("/test")
|
||||
async def test_ldap_connection(request: Request, db: Session = Depends(get_db)) -> Any:
|
||||
"""测试LDAP连接(管理员)"""
|
||||
"""
|
||||
测试 LDAP 连接
|
||||
|
||||
在保存配置前测试 LDAP 服务器连接是否正常。支持使用已保存的配置,
|
||||
也支持通过请求体覆盖任意配置项进行临时测试,而不影响已保存的配置。
|
||||
|
||||
**请求体字段**(均为可选,用于临时覆盖):
|
||||
- `server_url`: LDAP 服务器地址(覆盖已保存的配置)
|
||||
- `bind_dn`: 绑定 DN(覆盖已保存的配置)
|
||||
- `bind_password`: 绑定密码(覆盖已保存的密码)
|
||||
- `base_dn`: 搜索基准 DN(覆盖已保存的配置)
|
||||
- `user_search_filter`: 用户搜索过滤器(覆盖已保存的配置)
|
||||
- `username_attr`: 用户名属性(覆盖已保存的配置)
|
||||
- `email_attr`: 邮箱属性(覆盖已保存的配置)
|
||||
- `display_name_attr`: 显示名称属性(覆盖已保存的配置)
|
||||
- `use_starttls`: 是否使用 STARTTLS(覆盖已保存的配置)
|
||||
- `connect_timeout`: 连接超时时间(覆盖已保存的配置)
|
||||
|
||||
**测试逻辑**:
|
||||
- 未提供的字段使用已保存的配置值
|
||||
- `bind_password` 优先使用请求体中的值,否则使用已保存的加密密码
|
||||
- 测试时会尝试连接 LDAP 服务器并验证绑定 DN
|
||||
|
||||
**返回字段**:
|
||||
- `success`: 测试是否成功(布尔值)
|
||||
- `message`: 测试结果消息(成功或失败原因)
|
||||
"""
|
||||
adapter = AdminTestLDAPConnectionAdapter()
|
||||
return await pipeline.run(adapter=adapter, http_request=request, db=db, mode=adapter.mode)
|
||||
|
||||
|
||||
10
src/api/admin/management_tokens/__init__.py
Normal file
10
src/api/admin/management_tokens/__init__.py
Normal file
@@ -0,0 +1,10 @@
|
||||
"""Management Token 管理员路由模块"""
|
||||
|
||||
from fastapi import APIRouter
|
||||
|
||||
from .routes import router as management_tokens_router
|
||||
|
||||
router = APIRouter()
|
||||
router.include_router(management_tokens_router)
|
||||
|
||||
__all__ = ["router"]
|
||||
300
src/api/admin/management_tokens/routes.py
Normal file
300
src/api/admin/management_tokens/routes.py
Normal file
@@ -0,0 +1,300 @@
|
||||
"""管理员 Management Token 管理端点"""
|
||||
|
||||
from dataclasses import dataclass
|
||||
from typing import Optional
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException, Query, Request
|
||||
from fastapi.responses import JSONResponse
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from src.api.base.admin_adapter import AdminApiAdapter
|
||||
from src.api.base.context import ApiRequestContext
|
||||
from src.api.base.pipeline import ApiRequestPipeline
|
||||
from src.core.exceptions import NotFoundException
|
||||
from src.database import get_db
|
||||
from src.models.database import AuditEventType, ManagementToken, User
|
||||
from src.services.management_token import ManagementTokenService, token_to_dict
|
||||
|
||||
router = APIRouter(prefix="/api/admin/management-tokens", tags=["Admin - Management Tokens"])
|
||||
pipeline = ApiRequestPipeline()
|
||||
|
||||
|
||||
# ============== 安全基类 ==============
|
||||
|
||||
|
||||
class AdminManagementTokenApiAdapter(AdminApiAdapter):
|
||||
"""管理员 Management Token 管理 API 的基类
|
||||
|
||||
安全限制:禁止使用 Management Token 调用这些接口。
|
||||
"""
|
||||
|
||||
def authorize(self, context: ApiRequestContext) -> None:
|
||||
# 先调用父类的认证和权限检查
|
||||
super().authorize(context)
|
||||
|
||||
# 禁止使用 Management Token 调用 management-tokens 相关接口
|
||||
if context.management_token is not None:
|
||||
raise HTTPException(
|
||||
status_code=403,
|
||||
detail="不允许使用 Management Token 管理其他 Token,请使用 Web 界面或 JWT 认证",
|
||||
)
|
||||
|
||||
|
||||
# ============== 路由 ==============
|
||||
|
||||
|
||||
@router.get("")
|
||||
async def list_all_management_tokens(
|
||||
request: Request,
|
||||
user_id: Optional[str] = Query(None, description="筛选用户 ID"),
|
||||
is_active: Optional[bool] = Query(None, description="筛选激活状态"),
|
||||
skip: int = Query(0, ge=0),
|
||||
limit: int = Query(50, ge=1, le=100),
|
||||
db: Session = Depends(get_db),
|
||||
):
|
||||
"""列出所有 Management Tokens(管理员)
|
||||
|
||||
管理员查看所有用户的 Management Tokens,支持筛选和分页。
|
||||
|
||||
**查询参数**
|
||||
- user_id (Optional[str]): 筛选指定用户 ID 的 tokens
|
||||
- is_active (Optional[bool]): 筛选激活状态(true/false)
|
||||
- skip (int): 分页偏移量,默认 0
|
||||
- limit (int): 每页数量,范围 1-100,默认 50
|
||||
|
||||
**返回字段**
|
||||
- items (List[dict]): Token 列表
|
||||
- id (str): Token ID
|
||||
- user_id (str): 所属用户 ID
|
||||
- user (dict): 用户信息(包含 id, username, email 等)
|
||||
- name (str): Token 名称
|
||||
- description (Optional[str]): 描述
|
||||
- token_hash (str): Token 哈希值(不返回明文)
|
||||
- is_active (bool): 是否激活
|
||||
- allowed_ips (Optional[List[str]]): IP 白名单
|
||||
- expires_at (Optional[str]): 过期时间(ISO 8601 格式)
|
||||
- last_used_at (Optional[str]): 最后使用时间
|
||||
- created_at (str): 创建时间
|
||||
- updated_at (str): 更新时间
|
||||
- total (int): 总数量
|
||||
- skip (int): 当前偏移量
|
||||
- limit (int): 当前每页数量
|
||||
"""
|
||||
adapter = AdminListManagementTokensAdapter(
|
||||
user_id=user_id, is_active=is_active, skip=skip, limit=limit
|
||||
)
|
||||
return await pipeline.run(adapter=adapter, http_request=request, db=db, mode=adapter.mode)
|
||||
|
||||
|
||||
@router.get("/{token_id}")
|
||||
async def get_management_token(
|
||||
token_id: str,
|
||||
request: Request,
|
||||
db: Session = Depends(get_db),
|
||||
):
|
||||
"""获取 Management Token 详情(管理员)
|
||||
|
||||
管理员查看任意 Management Token 的详细信息。
|
||||
|
||||
**路径参数**
|
||||
- token_id (str): Token ID
|
||||
|
||||
**返回字段**
|
||||
- id (str): Token ID
|
||||
- user_id (str): 所属用户 ID
|
||||
- user (dict): 用户信息(包含 id, username, email 等)
|
||||
- name (str): Token 名称
|
||||
- description (Optional[str]): 描述
|
||||
- token_hash (str): Token 哈希值(不返回明文)
|
||||
- is_active (bool): 是否激活
|
||||
- allowed_ips (Optional[List[str]]): IP 白名单
|
||||
- expires_at (Optional[str]): 过期时间(ISO 8601 格式)
|
||||
- last_used_at (Optional[str]): 最后使用时间
|
||||
- created_at (str): 创建时间
|
||||
- updated_at (str): 更新时间
|
||||
"""
|
||||
adapter = AdminGetManagementTokenAdapter(token_id=token_id)
|
||||
return await pipeline.run(adapter=adapter, http_request=request, db=db, mode=adapter.mode)
|
||||
|
||||
|
||||
@router.delete("/{token_id}")
|
||||
async def delete_management_token(
|
||||
token_id: str, request: Request, db: Session = Depends(get_db)
|
||||
):
|
||||
"""删除任意 Management Token(管理员)
|
||||
|
||||
管理员可以删除任意用户的 Management Token。
|
||||
|
||||
**路径参数**
|
||||
- token_id (str): 要删除的 Token ID
|
||||
|
||||
**返回字段**
|
||||
- message (str): 操作结果消息
|
||||
"""
|
||||
adapter = AdminDeleteManagementTokenAdapter(token_id=token_id)
|
||||
return await pipeline.run(adapter=adapter, http_request=request, db=db, mode=adapter.mode)
|
||||
|
||||
|
||||
@router.patch("/{token_id}/status")
|
||||
async def toggle_management_token(
|
||||
token_id: str, request: Request, db: Session = Depends(get_db)
|
||||
):
|
||||
"""切换任意 Management Token 状态(管理员)
|
||||
|
||||
管理员可以启用/禁用任意用户的 Management Token。
|
||||
|
||||
**路径参数**
|
||||
- token_id (str): Token ID
|
||||
|
||||
**返回字段**
|
||||
- message (str): 操作结果消息("Token 已启用" 或 "Token 已禁用")
|
||||
- data (dict): 更新后的 Token 信息
|
||||
- id (str): Token ID
|
||||
- user_id (str): 所属用户 ID
|
||||
- user (dict): 用户信息
|
||||
- name (str): Token 名称
|
||||
- description (Optional[str]): 描述
|
||||
- token_hash (str): Token 哈希值
|
||||
- is_active (bool): 是否激活(已切换后的状态)
|
||||
- allowed_ips (Optional[List[str]]): IP 白名单
|
||||
- expires_at (Optional[str]): 过期时间
|
||||
- last_used_at (Optional[str]): 最后使用时间
|
||||
- created_at (str): 创建时间
|
||||
- updated_at (str): 更新时间
|
||||
"""
|
||||
adapter = AdminToggleManagementTokenAdapter(token_id=token_id)
|
||||
return await pipeline.run(adapter=adapter, http_request=request, db=db, mode=adapter.mode)
|
||||
|
||||
|
||||
# ============== 适配器 ==============
|
||||
|
||||
|
||||
@dataclass
|
||||
class AdminListManagementTokensAdapter(AdminManagementTokenApiAdapter):
|
||||
"""列出所有 Management Tokens"""
|
||||
|
||||
name: str = "admin_list_management_tokens"
|
||||
user_id: Optional[str] = None
|
||||
is_active: Optional[bool] = None
|
||||
skip: int = 0
|
||||
limit: int = 50
|
||||
|
||||
async def handle(self, context: ApiRequestContext):
|
||||
# 构建查询
|
||||
query = context.db.query(ManagementToken)
|
||||
|
||||
if self.user_id:
|
||||
query = query.filter(ManagementToken.user_id == self.user_id)
|
||||
if self.is_active is not None:
|
||||
query = query.filter(ManagementToken.is_active == self.is_active)
|
||||
|
||||
total = query.count()
|
||||
tokens = (
|
||||
query.order_by(ManagementToken.created_at.desc())
|
||||
.offset(self.skip)
|
||||
.limit(self.limit)
|
||||
.all()
|
||||
)
|
||||
|
||||
# 预加载用户信息
|
||||
user_ids = list(set(t.user_id for t in tokens))
|
||||
users = {u.id: u for u in context.db.query(User).filter(User.id.in_(user_ids)).all()}
|
||||
for token in tokens:
|
||||
token.user = users.get(token.user_id)
|
||||
|
||||
return JSONResponse(
|
||||
content={
|
||||
"items": [token_to_dict(t, include_user=True) for t in tokens],
|
||||
"total": total,
|
||||
"skip": self.skip,
|
||||
"limit": self.limit,
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@dataclass
|
||||
class AdminGetManagementTokenAdapter(AdminManagementTokenApiAdapter):
|
||||
"""获取 Management Token 详情"""
|
||||
|
||||
name: str = "admin_get_management_token"
|
||||
token_id: str = ""
|
||||
|
||||
async def handle(self, context: ApiRequestContext):
|
||||
token = ManagementTokenService.get_token_by_id(
|
||||
db=context.db, token_id=self.token_id
|
||||
)
|
||||
|
||||
if not token:
|
||||
raise NotFoundException("Management Token 不存在")
|
||||
|
||||
# 加载用户信息
|
||||
token.user = context.db.query(User).filter(User.id == token.user_id).first()
|
||||
|
||||
return JSONResponse(content=token_to_dict(token, include_user=True))
|
||||
|
||||
|
||||
@dataclass
|
||||
class AdminDeleteManagementTokenAdapter(AdminManagementTokenApiAdapter):
|
||||
"""删除 Management Token"""
|
||||
|
||||
name: str = "admin_delete_management_token"
|
||||
token_id: str = ""
|
||||
audit_success_event = AuditEventType.MANAGEMENT_TOKEN_DELETED
|
||||
|
||||
async def handle(self, context: ApiRequestContext):
|
||||
# 先获取 token 信息用于审计
|
||||
token = ManagementTokenService.get_token_by_id(
|
||||
db=context.db, token_id=self.token_id
|
||||
)
|
||||
|
||||
if not token:
|
||||
raise NotFoundException("Management Token 不存在")
|
||||
|
||||
context.add_audit_metadata(
|
||||
token_id=token.id,
|
||||
token_name=token.name,
|
||||
owner_user_id=token.user_id,
|
||||
)
|
||||
|
||||
success = ManagementTokenService.delete_token(
|
||||
db=context.db, token_id=self.token_id
|
||||
)
|
||||
|
||||
if not success:
|
||||
raise NotFoundException("Management Token 不存在")
|
||||
|
||||
return JSONResponse(content={"message": "删除成功"})
|
||||
|
||||
|
||||
@dataclass
|
||||
class AdminToggleManagementTokenAdapter(AdminManagementTokenApiAdapter):
|
||||
"""切换 Management Token 状态"""
|
||||
|
||||
name: str = "admin_toggle_management_token"
|
||||
token_id: str = ""
|
||||
audit_success_event = AuditEventType.MANAGEMENT_TOKEN_UPDATED
|
||||
|
||||
async def handle(self, context: ApiRequestContext):
|
||||
token = ManagementTokenService.toggle_status(
|
||||
db=context.db, token_id=self.token_id
|
||||
)
|
||||
|
||||
if not token:
|
||||
raise NotFoundException("Management Token 不存在")
|
||||
|
||||
# 加载用户信息
|
||||
token.user = context.db.query(User).filter(User.id == token.user_id).first()
|
||||
|
||||
context.add_audit_metadata(
|
||||
token_id=token.id,
|
||||
token_name=token.name,
|
||||
owner_user_id=token.user_id,
|
||||
is_active=token.is_active,
|
||||
)
|
||||
|
||||
return JSONResponse(
|
||||
content={
|
||||
"message": f"Token 已{'启用' if token.is_active else '禁用'}",
|
||||
"data": token_to_dict(token, include_user=True),
|
||||
}
|
||||
)
|
||||
@@ -8,7 +8,7 @@ from .catalog import router as catalog_router
|
||||
from .external import router as external_router
|
||||
from .global_models import router as global_models_router
|
||||
|
||||
router = APIRouter(prefix="/api/admin/models", tags=["Admin - Model Management"])
|
||||
router = APIRouter(prefix="/api/admin/models", tags=["Admin - Models"])
|
||||
|
||||
# 挂载子路由
|
||||
router.include_router(catalog_router)
|
||||
|
||||
@@ -31,6 +31,22 @@ async def get_model_catalog(
|
||||
request: Request,
|
||||
db: Session = Depends(get_db),
|
||||
) -> ModelCatalogResponse:
|
||||
"""
|
||||
获取统一模型目录
|
||||
|
||||
基于 GlobalModel 聚合所有活跃模型及其关联提供商的信息,返回完整的模型目录视图。
|
||||
|
||||
**返回字段**:
|
||||
- `models`: 模型列表,每个模型包含:
|
||||
- `global_model_name`: GlobalModel 名称
|
||||
- `display_name`: 显示名称
|
||||
- `description`: 模型描述
|
||||
- `providers`: 提供商列表,包含提供商名称、价格、能力等详细信息
|
||||
- `price_range`: 价格区间(基于 GlobalModel 第一阶梯价格)
|
||||
- `total_providers`: 关联提供商数量
|
||||
- `capabilities`: 模型能力标志(视觉、函数调用、流式输出)
|
||||
- `total`: 模型总数
|
||||
"""
|
||||
adapter = AdminGetModelCatalogAdapter()
|
||||
return await pipeline.run(adapter=adapter, http_request=request, db=db, mode=adapter.mode)
|
||||
|
||||
|
||||
@@ -82,9 +82,21 @@ def _mark_official_providers(data: dict[str, Any]) -> dict[str, Any]:
|
||||
@router.get("/external")
|
||||
async def get_external_models(_: User = Depends(require_admin)) -> JSONResponse:
|
||||
"""
|
||||
获取 models.dev 的模型数据(代理请求,解决跨域问题)
|
||||
数据缓存 15 分钟(使用 Redis,多 worker 共享)
|
||||
每个提供商会标记 official 字段,前端可据此过滤
|
||||
获取外部模型数据
|
||||
|
||||
从 models.dev 获取第三方模型数据,用于导入新模型或参考定价信息。
|
||||
该接口作为代理请求解决跨域问题,并提供缓存优化。
|
||||
|
||||
**功能特性**:
|
||||
- 代理 models.dev API,解决前端跨域问题
|
||||
- 使用 Redis 缓存 15 分钟,多 worker 共享缓存
|
||||
- 自动标记官方提供商(official 字段),前端可据此过滤第三方转售商
|
||||
|
||||
**返回字段**:
|
||||
- 键为提供商 ID(如 "anthropic"、"openai")
|
||||
- 值为提供商详细信息,包含:
|
||||
- `official`: 是否为官方提供商(true/false)
|
||||
- 其他 models.dev 提供的原始字段(模型列表、定价等)
|
||||
"""
|
||||
# 检查缓存
|
||||
cached = await _get_cached_data()
|
||||
@@ -130,7 +142,16 @@ async def get_external_models(_: User = Depends(require_admin)) -> JSONResponse:
|
||||
|
||||
@router.delete("/external/cache")
|
||||
async def clear_external_models_cache(_: User = Depends(require_admin)) -> dict:
|
||||
"""清除 models.dev 缓存"""
|
||||
"""
|
||||
清除外部模型数据缓存
|
||||
|
||||
手动清除 models.dev 的 Redis 缓存,强制下次请求重新获取最新数据。
|
||||
通常用于需要立即更新外部模型数据的场景。
|
||||
|
||||
**返回字段**:
|
||||
- `cleared`: 是否成功清除缓存(true/false)
|
||||
- `message`: 提示信息(仅在 Redis 未启用时返回)
|
||||
"""
|
||||
redis = await get_redis_client()
|
||||
if redis is None:
|
||||
return {"cleared": False, "message": "Redis 未启用"}
|
||||
|
||||
@@ -40,7 +40,27 @@ async def list_global_models(
|
||||
search: Optional[str] = Query(None),
|
||||
db: Session = Depends(get_db),
|
||||
) -> GlobalModelListResponse:
|
||||
"""获取 GlobalModel 列表"""
|
||||
"""
|
||||
获取 GlobalModel 列表
|
||||
|
||||
查询系统中的全局模型列表,支持分页、过滤和搜索功能。
|
||||
|
||||
**查询参数**:
|
||||
- `skip`: 跳过记录数,用于分页(默认 0)
|
||||
- `limit`: 返回记录数,用于分页(默认 100,最大 1000)
|
||||
- `is_active`: 过滤活跃状态(true/false/null,null 表示不过滤)
|
||||
- `search`: 搜索关键词,支持按名称或显示名称模糊搜索
|
||||
|
||||
**返回字段**:
|
||||
- `models`: GlobalModel 列表,每个包含:
|
||||
- `id`: GlobalModel ID
|
||||
- `name`: 模型名称(唯一)
|
||||
- `display_name`: 显示名称
|
||||
- `is_active`: 是否活跃
|
||||
- `provider_count`: 关联提供商数量
|
||||
- 定价和能力配置等其他字段
|
||||
- `total`: 返回的模型总数
|
||||
"""
|
||||
adapter = AdminListGlobalModelsAdapter(
|
||||
skip=skip,
|
||||
limit=limit,
|
||||
@@ -56,7 +76,21 @@ async def get_global_model(
|
||||
global_model_id: str,
|
||||
db: Session = Depends(get_db),
|
||||
) -> GlobalModelWithStats:
|
||||
"""获取单个 GlobalModel 详情(含统计信息)"""
|
||||
"""
|
||||
获取单个 GlobalModel 详情
|
||||
|
||||
查询指定 GlobalModel 的详细信息,包含关联的提供商和价格统计数据。
|
||||
|
||||
**路径参数**:
|
||||
- `global_model_id`: GlobalModel ID
|
||||
|
||||
**返回字段**:
|
||||
- 基础字段:`id`, `name`, `display_name`, `is_active` 等
|
||||
- 统计字段:
|
||||
- `total_models`: 关联的 Model 实现数量
|
||||
- `total_providers`: 关联的提供商数量
|
||||
- `price_range`: 价格区间统计(最低/最高输入输出价格)
|
||||
"""
|
||||
adapter = AdminGetGlobalModelAdapter(global_model_id=global_model_id)
|
||||
return await pipeline.run(adapter=adapter, http_request=request, db=db, mode=adapter.mode)
|
||||
|
||||
@@ -67,7 +101,24 @@ async def create_global_model(
|
||||
payload: GlobalModelCreate,
|
||||
db: Session = Depends(get_db),
|
||||
) -> GlobalModelResponse:
|
||||
"""创建 GlobalModel"""
|
||||
"""
|
||||
创建 GlobalModel
|
||||
|
||||
创建一个新的全局模型定义,作为多个提供商实现的统一抽象。
|
||||
|
||||
**请求体字段**:
|
||||
- `name`: 模型名称(唯一标识,如 "claude-3-5-sonnet-20241022")
|
||||
- `display_name`: 显示名称(如 "Claude 3.5 Sonnet")
|
||||
- `is_active`: 是否活跃(默认 true)
|
||||
- `default_price_per_request`: 默认按次计费价格(可选)
|
||||
- `default_tiered_pricing`: 默认阶梯定价配置(包含多个价格阶梯)
|
||||
- `supported_capabilities`: 支持的能力标志(vision、function_calling、streaming)
|
||||
- `config`: 额外配置(JSON 格式,如 description、context_window 等)
|
||||
|
||||
**返回字段**:
|
||||
- `id`: 创建的 GlobalModel ID
|
||||
- 其他请求体中的所有字段
|
||||
"""
|
||||
adapter = AdminCreateGlobalModelAdapter(payload=payload)
|
||||
return await pipeline.run(adapter=adapter, http_request=request, db=db, mode=adapter.mode)
|
||||
|
||||
@@ -79,7 +130,26 @@ async def update_global_model(
|
||||
payload: GlobalModelUpdate,
|
||||
db: Session = Depends(get_db),
|
||||
) -> GlobalModelResponse:
|
||||
"""更新 GlobalModel"""
|
||||
"""
|
||||
更新 GlobalModel
|
||||
|
||||
更新指定 GlobalModel 的配置信息,支持部分字段更新。
|
||||
更新后会自动失效相关缓存。
|
||||
|
||||
**路径参数**:
|
||||
- `global_model_id`: GlobalModel ID
|
||||
|
||||
**请求体字段**(均为可选):
|
||||
- `display_name`: 显示名称
|
||||
- `is_active`: 是否活跃
|
||||
- `default_price_per_request`: 默认按次计费价格
|
||||
- `default_tiered_pricing`: 默认阶梯定价配置
|
||||
- `supported_capabilities`: 支持的能力标志
|
||||
- `config`: 额外配置
|
||||
|
||||
**返回字段**:
|
||||
- 更新后的完整 GlobalModel 信息
|
||||
"""
|
||||
adapter = AdminUpdateGlobalModelAdapter(global_model_id=global_model_id, payload=payload)
|
||||
return await pipeline.run(adapter=adapter, http_request=request, db=db, mode=adapter.mode)
|
||||
|
||||
@@ -90,7 +160,18 @@ async def delete_global_model(
|
||||
global_model_id: str,
|
||||
db: Session = Depends(get_db),
|
||||
):
|
||||
"""删除 GlobalModel(级联删除所有关联的 Provider 模型实现)"""
|
||||
"""
|
||||
删除 GlobalModel
|
||||
|
||||
删除指定的 GlobalModel,会级联删除所有关联的 Provider 模型实现。
|
||||
删除后会自动失效相关缓存。
|
||||
|
||||
**路径参数**:
|
||||
- `global_model_id`: GlobalModel ID
|
||||
|
||||
**返回**:
|
||||
- 成功删除返回 204 状态码,无响应体
|
||||
"""
|
||||
adapter = AdminDeleteGlobalModelAdapter(global_model_id=global_model_id)
|
||||
await pipeline.run(adapter=adapter, http_request=request, db=db, mode=adapter.mode)
|
||||
return None
|
||||
@@ -105,7 +186,29 @@ async def batch_assign_to_providers(
|
||||
payload: BatchAssignToProvidersRequest,
|
||||
db: Session = Depends(get_db),
|
||||
) -> BatchAssignToProvidersResponse:
|
||||
"""批量为多个 Provider 添加 GlobalModel 实现"""
|
||||
"""
|
||||
批量为提供商添加模型实现
|
||||
|
||||
为指定的 GlobalModel 批量创建多个 Provider 的模型实现(Model 记录)。
|
||||
用于快速将一个统一模型分配给多个提供商。
|
||||
|
||||
**路径参数**:
|
||||
- `global_model_id`: GlobalModel ID
|
||||
|
||||
**请求体字段**:
|
||||
- `provider_ids`: 提供商 ID 列表
|
||||
- `create_models`: Model 创建配置列表,每个包含:
|
||||
- `provider_id`: 提供商 ID
|
||||
- `provider_model_name`: 提供商侧的模型名称(如 "claude-3-5-sonnet-20241022")
|
||||
- 其他可选字段(价格覆盖、能力覆盖等)
|
||||
|
||||
**返回字段**:
|
||||
- `success`: 成功创建的 Model 列表
|
||||
- `errors`: 失败的提供商及错误信息列表
|
||||
- `total_requested`: 请求处理的总数
|
||||
- `total_success`: 成功创建的数量
|
||||
- `total_errors`: 失败的数量
|
||||
"""
|
||||
adapter = AdminBatchAssignToProvidersAdapter(global_model_id=global_model_id, payload=payload)
|
||||
return await pipeline.run(adapter=adapter, http_request=request, db=db, mode=adapter.mode)
|
||||
|
||||
@@ -116,7 +219,27 @@ async def get_global_model_providers(
|
||||
global_model_id: str,
|
||||
db: Session = Depends(get_db),
|
||||
) -> GlobalModelProvidersResponse:
|
||||
"""获取 GlobalModel 的所有关联提供商(包括非活跃的)"""
|
||||
"""
|
||||
获取 GlobalModel 的关联提供商
|
||||
|
||||
查询指定 GlobalModel 的所有关联提供商及其模型实现详情,包括非活跃的提供商。
|
||||
用于查看某个统一模型在各个提供商上的具体配置。
|
||||
|
||||
**路径参数**:
|
||||
- `global_model_id`: GlobalModel ID
|
||||
|
||||
**返回字段**:
|
||||
- `providers`: 提供商列表,每个包含:
|
||||
- `provider_id`: 提供商 ID
|
||||
- `provider_name`: 提供商名称
|
||||
- `provider_display_name`: 提供商显示名称
|
||||
- `model_id`: Model 实现 ID
|
||||
- `target_model`: 提供商侧的模型名称
|
||||
- 价格信息(input_price_per_1m、output_price_per_1m 等)
|
||||
- 能力标志(supports_vision、supports_function_calling、supports_streaming)
|
||||
- `is_active`: 是否活跃
|
||||
- `total`: 关联提供商总数
|
||||
"""
|
||||
adapter = AdminGetGlobalModelProvidersAdapter(global_model_id=global_model_id)
|
||||
return await pipeline.run(adapter=adapter, http_request=request, db=db, mode=adapter.mode)
|
||||
|
||||
|
||||
@@ -39,6 +39,34 @@ async def get_audit_logs(
|
||||
offset: int = Query(0, description="偏移量"),
|
||||
db: Session = Depends(get_db),
|
||||
):
|
||||
"""
|
||||
获取审计日志
|
||||
|
||||
获取系统审计日志列表,支持按用户、事件类型、时间范围筛选。需要管理员权限。
|
||||
|
||||
**查询参数**:
|
||||
- `user_id`: 可选,用户 ID 筛选(UUID 格式)
|
||||
- `event_type`: 可选,事件类型筛选
|
||||
- `days`: 查询最近多少天的日志,默认 7 天
|
||||
- `limit`: 返回数量限制,默认 100
|
||||
- `offset`: 分页偏移量,默认 0
|
||||
|
||||
**返回字段**:
|
||||
- `items`: 审计日志列表,每条日志包含:
|
||||
- `id`: 日志 ID
|
||||
- `event_type`: 事件类型
|
||||
- `user_id`: 用户 ID
|
||||
- `user_email`: 用户邮箱
|
||||
- `user_username`: 用户名
|
||||
- `description`: 事件描述
|
||||
- `ip_address`: IP 地址
|
||||
- `status_code`: HTTP 状态码
|
||||
- `error_message`: 错误信息
|
||||
- `metadata`: 事件元数据
|
||||
- `created_at`: 创建时间
|
||||
- `meta`: 分页元数据(total, limit, offset, count)
|
||||
- `filters`: 筛选条件
|
||||
"""
|
||||
adapter = AdminGetAuditLogsAdapter(
|
||||
user_id=user_id,
|
||||
event_type=event_type,
|
||||
@@ -51,6 +79,19 @@ async def get_audit_logs(
|
||||
|
||||
@router.get("/system-status")
|
||||
async def get_system_status(request: Request, db: Session = Depends(get_db)):
|
||||
"""
|
||||
获取系统状态
|
||||
|
||||
获取系统当前的运行状态和关键指标。需要管理员权限。
|
||||
|
||||
**返回字段**:
|
||||
- `timestamp`: 当前时间戳
|
||||
- `users`: 用户统计(total: 总用户数, active: 活跃用户数)
|
||||
- `providers`: 提供商统计(total: 总提供商数, active: 活跃提供商数)
|
||||
- `api_keys`: API Key 统计(total: 总数, active: 活跃数)
|
||||
- `today_stats`: 今日统计(requests: 请求数, tokens: token 数, cost_usd: 成本)
|
||||
- `recent_errors`: 最近 1 小时内的错误数
|
||||
"""
|
||||
adapter = AdminSystemStatusAdapter()
|
||||
return await pipeline.run(adapter=adapter, http_request=request, db=db, mode=adapter.mode)
|
||||
|
||||
@@ -61,6 +102,26 @@ async def get_suspicious_activities(
|
||||
hours: int = Query(24, description="时间范围(小时)"),
|
||||
db: Session = Depends(get_db),
|
||||
):
|
||||
"""
|
||||
获取可疑活动记录
|
||||
|
||||
获取系统检测到的可疑活动记录。需要管理员权限。
|
||||
|
||||
**查询参数**:
|
||||
- `hours`: 时间范围(小时),默认 24 小时
|
||||
|
||||
**返回字段**:
|
||||
- `activities`: 可疑活动列表,每条记录包含:
|
||||
- `id`: 记录 ID
|
||||
- `event_type`: 事件类型
|
||||
- `user_id`: 用户 ID
|
||||
- `description`: 事件描述
|
||||
- `ip_address`: IP 地址
|
||||
- `metadata`: 事件元数据
|
||||
- `created_at`: 创建时间
|
||||
- `count`: 活动总数
|
||||
- `time_range_hours`: 查询的时间范围(小时)
|
||||
"""
|
||||
adapter = AdminSuspiciousActivitiesAdapter(hours=hours)
|
||||
return await pipeline.run(adapter=adapter, http_request=request, db=db, mode=adapter.mode)
|
||||
|
||||
@@ -72,19 +133,56 @@ async def analyze_user_behavior(
|
||||
days: int = Query(30, description="分析天数"),
|
||||
db: Session = Depends(get_db),
|
||||
):
|
||||
"""
|
||||
分析用户行为
|
||||
|
||||
分析指定用户的行为模式和使用情况。需要管理员权限。
|
||||
|
||||
**路径参数**:
|
||||
- `user_id`: 用户 ID
|
||||
|
||||
**查询参数**:
|
||||
- `days`: 分析最近多少天的数据,默认 30 天
|
||||
|
||||
**返回字段**:
|
||||
- 用户行为分析结果,包括活动频率、使用模式、异常行为等
|
||||
"""
|
||||
adapter = AdminUserBehaviorAdapter(user_id=user_id, days=days)
|
||||
return await pipeline.run(adapter=adapter, http_request=request, db=db, mode=adapter.mode)
|
||||
|
||||
|
||||
@router.get("/resilience-status")
|
||||
async def get_resilience_status(request: Request, db: Session = Depends(get_db)):
|
||||
"""
|
||||
获取韧性系统状态
|
||||
|
||||
获取系统韧性管理的当前状态,包括错误统计、熔断器状态等。需要管理员权限。
|
||||
|
||||
**返回字段**:
|
||||
- `timestamp`: 当前时间戳
|
||||
- `health_score`: 健康评分(0-100)
|
||||
- `status`: 系统状态(healthy: 健康,degraded: 降级,critical: 严重)
|
||||
- `error_statistics`: 错误统计信息
|
||||
- `recent_errors`: 最近的错误列表(最多 10 条)
|
||||
- `recommendations`: 系统建议
|
||||
"""
|
||||
adapter = AdminResilienceStatusAdapter()
|
||||
return await pipeline.run(adapter=adapter, http_request=request, db=db, mode=adapter.mode)
|
||||
|
||||
|
||||
@router.delete("/resilience/error-stats")
|
||||
async def reset_error_stats(request: Request, db: Session = Depends(get_db)):
|
||||
"""Reset resilience error statistics"""
|
||||
"""
|
||||
重置错误统计
|
||||
|
||||
重置韧性系统的错误统计数据。需要管理员权限。
|
||||
|
||||
**返回字段**:
|
||||
- `message`: 操作结果信息
|
||||
- `previous_stats`: 重置前的统计数据
|
||||
- `reset_by`: 执行重置的管理员邮箱
|
||||
- `reset_at`: 重置时间
|
||||
"""
|
||||
adapter = AdminResetErrorStatsAdapter()
|
||||
return await pipeline.run(adapter=adapter, http_request=request, db=db, mode=adapter.mode)
|
||||
|
||||
@@ -95,6 +193,18 @@ async def get_circuit_history(
|
||||
limit: int = Query(50, ge=1, le=200),
|
||||
db: Session = Depends(get_db),
|
||||
):
|
||||
"""
|
||||
获取熔断器历史记录
|
||||
|
||||
获取熔断器的状态变更历史记录。需要管理员权限。
|
||||
|
||||
**查询参数**:
|
||||
- `limit`: 返回数量限制,默认 50,最大 200
|
||||
|
||||
**返回字段**:
|
||||
- `items`: 熔断器历史记录列表
|
||||
- `count`: 记录总数
|
||||
"""
|
||||
adapter = AdminCircuitHistoryAdapter(limit=limit)
|
||||
return await pipeline.run(adapter=adapter, http_request=request, db=db, mode=adapter.mode)
|
||||
|
||||
|
||||
@@ -117,12 +117,21 @@ async def get_cache_stats(
|
||||
"""
|
||||
获取缓存亲和性统计信息
|
||||
|
||||
返回:
|
||||
- 缓存命中率
|
||||
- 缓存用户数
|
||||
- Provider切换次数
|
||||
- Key切换次数
|
||||
- 缓存预留配置
|
||||
获取缓存调度器的运行统计数据,包括命中率、切换次数、调度器配置等。
|
||||
用于监控缓存亲和性功能的运行状态和性能指标。
|
||||
|
||||
**返回字段**:
|
||||
- `status`: 状态(ok)
|
||||
- `data`: 统计数据对象
|
||||
- `scheduler`: 调度器名称(cache_aware 或 random)
|
||||
- `total_affinities`: 总缓存亲和性数量
|
||||
- `cache_hit_rate`: 缓存命中率(0.0-1.0)
|
||||
- `provider_switches`: Provider 切换次数
|
||||
- `key_switches`: Key 切换次数
|
||||
- `cache_hits`: 缓存命中次数
|
||||
- `cache_misses`: 缓存未命中次数
|
||||
- `scheduler_metrics`: 调度器详细指标
|
||||
- `affinity_stats`: 亲和性统计数据
|
||||
"""
|
||||
adapter = AdminCacheStatsAdapter()
|
||||
return await pipeline.run(adapter=adapter, http_request=request, db=db, mode=adapter.mode)
|
||||
@@ -137,16 +146,33 @@ async def get_user_affinity(
|
||||
"""
|
||||
查询指定用户的所有缓存亲和性
|
||||
|
||||
参数:
|
||||
- user_identifier: 用户标识符,支持以下格式:
|
||||
* 用户名 (username),如: yuanhonghu
|
||||
* 邮箱 (email),如: user@example.com
|
||||
* 用户UUID (user_id),如: 550e8400-e29b-41d4-a716-446655440000
|
||||
* API Key ID,如: 660e8400-e29b-41d4-a716-446655440000
|
||||
根据用户标识符查询该用户在各个端点上的缓存亲和性记录。
|
||||
支持多种标识符格式的自动识别和解析。
|
||||
|
||||
返回:
|
||||
- 用户信息
|
||||
- 所有端点的缓存亲和性列表(每个端点一条记录)
|
||||
**路径参数**:
|
||||
- `user_identifier`: 用户标识符,支持以下格式:
|
||||
- 用户名(username),如:yuanhonghu
|
||||
- 邮箱(email),如:user@example.com
|
||||
- 用户 UUID(user_id),如:550e8400-e29b-41d4-a716-446655440000
|
||||
- API Key ID,如:660e8400-e29b-41d4-a716-446655440000
|
||||
|
||||
**返回字段**:
|
||||
- `status`: 状态(ok 或 not_found)
|
||||
- `message`: 提示消息(当无缓存时)
|
||||
- `user_info`: 用户信息
|
||||
- `user_id`: 用户 ID
|
||||
- `username`: 用户名
|
||||
- `email`: 邮箱
|
||||
- `affinities`: 缓存亲和性列表
|
||||
- `provider_id`: Provider ID
|
||||
- `endpoint_id`: Endpoint ID
|
||||
- `key_id`: Key ID
|
||||
- `api_format`: API 格式
|
||||
- `model_name`: 模型名称(global_model_id)
|
||||
- `created_at`: 创建时间
|
||||
- `expire_at`: 过期时间
|
||||
- `request_count`: 请求计数
|
||||
- `total_endpoints`: 缓存的端点数量
|
||||
"""
|
||||
adapter = AdminGetUserAffinityAdapter(user_identifier=user_identifier)
|
||||
return await pipeline.run(adapter=adapter, http_request=request, db=db, mode=adapter.mode)
|
||||
@@ -161,10 +187,50 @@ async def list_affinities(
|
||||
db: Session = Depends(get_db),
|
||||
) -> Any:
|
||||
"""
|
||||
获取所有缓存亲和性列表,可选按关键词过滤
|
||||
获取所有缓存亲和性列表
|
||||
|
||||
参数:
|
||||
- keyword: 可选,支持用户名/邮箱/User ID/API Key ID 或模糊匹配
|
||||
查询系统中所有的缓存亲和性记录,支持按关键词过滤和分页。
|
||||
返回详细的用户、Provider、Endpoint、Key 信息。
|
||||
|
||||
**查询参数**:
|
||||
- `keyword`: 可选,支持以下过滤方式(可选)
|
||||
- 用户名/邮箱/User ID/API Key ID(精确匹配)
|
||||
- 任意字段的模糊匹配(affinity_key、user_id、username、email、provider_id、key_id)
|
||||
- `limit`: 返回数量限制(1-1000,默认 100)
|
||||
- `offset`: 偏移量(用于分页,默认 0)
|
||||
|
||||
**返回字段**:
|
||||
- `status`: 状态(ok)
|
||||
- `data`: 分页数据对象
|
||||
- `items`: 缓存亲和性列表
|
||||
- `affinity_key`: API Key ID(用于缓存键)
|
||||
- `user_api_key_name`: 用户 API Key 名称
|
||||
- `user_api_key_prefix`: 脱敏后的用户 API Key
|
||||
- `is_standalone`: 是否为独立 API Key
|
||||
- `user_id`: 用户 ID
|
||||
- `username`: 用户名
|
||||
- `email`: 邮箱
|
||||
- `provider_id`: Provider ID
|
||||
- `provider_name`: Provider 显示名称
|
||||
- `endpoint_id`: Endpoint ID
|
||||
- `endpoint_api_format`: Endpoint API 格式
|
||||
- `endpoint_url`: Endpoint 基础 URL
|
||||
- `key_id`: Key ID
|
||||
- `key_name`: Key 名称
|
||||
- `key_prefix`: 脱敏后的 Provider Key
|
||||
- `rate_multiplier`: 速率倍数
|
||||
- `global_model_id`: GlobalModel ID
|
||||
- `model_name`: 模型名称
|
||||
- `model_display_name`: 模型显示名称
|
||||
- `api_format`: API 格式
|
||||
- `created_at`: 创建时间
|
||||
- `expire_at`: 过期时间
|
||||
- `request_count`: 请求计数
|
||||
- `meta`: 分页元数据
|
||||
- `count`: 总数量
|
||||
- `limit`: 每页数量
|
||||
- `offset`: 当前偏移量
|
||||
- `matched_user_id`: 匹配到的用户 ID(当关键词为用户标识时)
|
||||
"""
|
||||
adapter = AdminListAffinitiesAdapter(keyword=keyword, limit=limit, offset=offset)
|
||||
return await pipeline.run(adapter=adapter, http_request=request, db=db, mode=adapter.mode)
|
||||
@@ -177,10 +243,27 @@ async def clear_user_cache(
|
||||
db: Session = Depends(get_db),
|
||||
) -> Any:
|
||||
"""
|
||||
Clear cache affinity for a specific user
|
||||
清除指定用户的缓存亲和性
|
||||
|
||||
Parameters:
|
||||
- user_identifier: User identifier (username, email, user_id, or API Key ID)
|
||||
清除指定用户或 API Key 的所有缓存亲和性记录。
|
||||
支持按用户维度或单个 API Key 维度清除。
|
||||
|
||||
**路径参数**:
|
||||
- `user_identifier`: 用户标识符,支持以下格式:
|
||||
- 用户名(username)
|
||||
- 邮箱(email)
|
||||
- 用户 UUID(user_id)
|
||||
- API Key ID(清除该 API Key 的缓存)
|
||||
|
||||
**返回字段**:
|
||||
- `status`: 状态(ok)
|
||||
- `message`: 操作结果消息
|
||||
- `user_info`: 用户信息
|
||||
- `user_id`: 用户 ID
|
||||
- `username`: 用户名
|
||||
- `email`: 邮箱
|
||||
- `api_key_id`: API Key ID(当清除单个 API Key 时)
|
||||
- `api_key_name`: API Key 名称(当清除单个 API Key 时)
|
||||
"""
|
||||
adapter = AdminClearUserCacheAdapter(user_identifier=user_identifier)
|
||||
return await pipeline.run(adapter=adapter, http_request=request, db=db, mode=adapter.mode)
|
||||
@@ -196,13 +279,23 @@ async def clear_single_affinity(
|
||||
db: Session = Depends(get_db),
|
||||
) -> Any:
|
||||
"""
|
||||
Clear a single cache affinity entry
|
||||
清除单条缓存亲和性记录
|
||||
|
||||
Parameters:
|
||||
- affinity_key: API Key ID
|
||||
- endpoint_id: Endpoint ID
|
||||
- model_id: Model ID (GlobalModel ID)
|
||||
- api_format: API format (claude/openai)
|
||||
根据精确的缓存键(affinity_key + endpoint_id + model_id + api_format)
|
||||
清除单条缓存亲和性记录。用于精确控制缓存清除。
|
||||
|
||||
**路径参数**:
|
||||
- `affinity_key`: API Key ID(用于缓存的键)
|
||||
- `endpoint_id`: Endpoint ID
|
||||
- `model_id`: GlobalModel ID
|
||||
- `api_format`: API 格式(如:claude、openai、gemini)
|
||||
|
||||
**返回字段**:
|
||||
- `status`: 状态(ok)
|
||||
- `message`: 操作结果消息
|
||||
- `affinity_key`: API Key ID
|
||||
- `endpoint_id`: Endpoint ID
|
||||
- `model_id`: GlobalModel ID
|
||||
"""
|
||||
adapter = AdminClearSingleAffinityAdapter(
|
||||
affinity_key=affinity_key, endpoint_id=endpoint_id, model_id=model_id, api_format=api_format
|
||||
@@ -216,9 +309,17 @@ async def clear_all_cache(
|
||||
db: Session = Depends(get_db),
|
||||
) -> Any:
|
||||
"""
|
||||
Clear all cache affinities
|
||||
清除所有缓存亲和性
|
||||
|
||||
Warning: This affects all users, use with caution
|
||||
清除系统中所有用户的缓存亲和性记录。此操作会影响所有用户,
|
||||
下次请求时将重新建立缓存亲和性。请谨慎使用。
|
||||
|
||||
**警告**: 此操作影响所有用户,使用前请确认
|
||||
|
||||
**返回字段**:
|
||||
- `status`: 状态(ok)
|
||||
- `message`: 操作结果消息
|
||||
- `count`: 清除的缓存数量
|
||||
"""
|
||||
adapter = AdminClearAllCacheAdapter()
|
||||
return await pipeline.run(adapter=adapter, http_request=request, db=db, mode=adapter.mode)
|
||||
@@ -231,10 +332,19 @@ async def clear_provider_cache(
|
||||
db: Session = Depends(get_db),
|
||||
) -> Any:
|
||||
"""
|
||||
Clear cache affinities for a specific provider
|
||||
清除指定 Provider 的缓存亲和性
|
||||
|
||||
Parameters:
|
||||
- provider_id: Provider ID
|
||||
清除与指定 Provider 相关的所有缓存亲和性记录。
|
||||
当 Provider 配置变更或下线时使用。
|
||||
|
||||
**路径参数**:
|
||||
- `provider_id`: Provider ID
|
||||
|
||||
**返回字段**:
|
||||
- `status`: 状态(ok)
|
||||
- `message`: 操作结果消息
|
||||
- `provider_id`: Provider ID
|
||||
- `count`: 清除的缓存数量
|
||||
"""
|
||||
adapter = AdminClearProviderCacheAdapter(provider_id=provider_id)
|
||||
return await pipeline.run(adapter=adapter, http_request=request, db=db, mode=adapter.mode)
|
||||
@@ -248,9 +358,25 @@ async def get_cache_config(
|
||||
"""
|
||||
获取缓存相关配置
|
||||
|
||||
返回:
|
||||
- 缓存TTL
|
||||
- 缓存预留比例
|
||||
获取缓存亲和性功能的配置参数,包括缓存 TTL、预留比例、
|
||||
动态预留机制配置等。
|
||||
|
||||
**返回字段**:
|
||||
- `status`: 状态(ok)
|
||||
- `data`: 配置数据
|
||||
- `cache_ttl_seconds`: 缓存亲和性有效期(秒)
|
||||
- `cache_reservation_ratio`: 静态预留比例(已被动态预留替代)
|
||||
- `dynamic_reservation`: 动态预留机制配置
|
||||
- `enabled`: 是否启用
|
||||
- `config`: 配置参数
|
||||
- `probe_phase_requests`: 探测阶段请求数阈值
|
||||
- `probe_reservation`: 探测阶段预留比例
|
||||
- `stable_min_reservation`: 稳定阶段最小预留比例
|
||||
- `stable_max_reservation`: 稳定阶段最大预留比例
|
||||
- `low_load_threshold`: 低负载阈值
|
||||
- `high_load_threshold`: 高负载阈值
|
||||
- `description`: 各参数说明
|
||||
- `description`: 配置说明
|
||||
"""
|
||||
adapter = AdminCacheConfigAdapter()
|
||||
return await pipeline.run(adapter=adapter, http_request=request, db=db, mode=adapter.mode)
|
||||
@@ -262,7 +388,31 @@ async def get_cache_metrics(
|
||||
db: Session = Depends(get_db),
|
||||
) -> Any:
|
||||
"""
|
||||
以 Prometheus 文本格式暴露缓存调度指标,方便接入 Grafana。
|
||||
获取缓存调度指标(Prometheus 格式)
|
||||
|
||||
以 Prometheus 文本格式输出缓存调度器的监控指标,
|
||||
方便接入 Prometheus/Grafana 等监控系统。
|
||||
|
||||
**返回格式**: Prometheus 文本格式(Content-Type: text/plain)
|
||||
|
||||
**指标列表**:
|
||||
- `cache_scheduler_total_batches`: 总批次数
|
||||
- `cache_scheduler_last_batch_size`: 最后一批候选数
|
||||
- `cache_scheduler_total_candidates`: 总候选数
|
||||
- `cache_scheduler_last_candidate_count`: 最后一批候选计数
|
||||
- `cache_scheduler_cache_hits`: 缓存命中次数
|
||||
- `cache_scheduler_cache_misses`: 缓存未命中次数
|
||||
- `cache_scheduler_cache_hit_rate`: 缓存命中率
|
||||
- `cache_scheduler_concurrency_denied`: 并发拒绝次数
|
||||
- `cache_scheduler_avg_candidates_per_batch`: 平均每批候选数
|
||||
- `cache_affinity_total`: 总缓存亲和性数量
|
||||
- `cache_affinity_hits`: 亲和性命中次数
|
||||
- `cache_affinity_misses`: 亲和性未命中次数
|
||||
- `cache_affinity_hit_rate`: 亲和性命中率
|
||||
- `cache_affinity_invalidations`: 亲和性失效次数
|
||||
- `cache_affinity_provider_switches`: Provider 切换次数
|
||||
- `cache_affinity_key_switches`: Key 切换次数
|
||||
- `cache_scheduler_info`: 调度器信息(label: scheduler)
|
||||
"""
|
||||
adapter = AdminCacheMetricsAdapter()
|
||||
return await pipeline.run(adapter=adapter, http_request=request, db=db, mode=adapter.mode)
|
||||
@@ -998,10 +1148,39 @@ async def get_model_mapping_cache_stats(
|
||||
"""
|
||||
获取模型映射缓存统计信息
|
||||
|
||||
返回:
|
||||
- 缓存键数量
|
||||
- 缓存 TTL 配置
|
||||
- 各类型缓存数量
|
||||
获取模型解析缓存的详细统计信息,包括各类型缓存键数量、
|
||||
映射关系列表、Provider 级别的模型映射缓存等。
|
||||
|
||||
**返回字段**:
|
||||
- `status`: 状态(ok)
|
||||
- `data`: 统计数据
|
||||
- `available`: Redis 是否可用
|
||||
- `message`: 提示消息(当 Redis 未启用时)
|
||||
- `ttl_seconds`: 缓存 TTL(秒)
|
||||
- `total_keys`: 总缓存键数量
|
||||
- `breakdown`: 各类型缓存键数量分解
|
||||
- `model_by_id`: Model ID 缓存数量
|
||||
- `model_by_provider_global`: Provider-GlobalModel 缓存数量
|
||||
- `global_model_by_id`: GlobalModel ID 缓存数量
|
||||
- `global_model_by_name`: GlobalModel 名称缓存数量
|
||||
- `global_model_resolve`: GlobalModel 解析缓存数量
|
||||
- `mappings`: 模型映射列表(最多 100 条)
|
||||
- `mapping_name`: 映射名称(别名)
|
||||
- `global_model_name`: GlobalModel 名称
|
||||
- `global_model_display_name`: GlobalModel 显示名称
|
||||
- `providers`: 使用该映射的 Provider 列表
|
||||
- `ttl`: 缓存剩余 TTL(秒)
|
||||
- `provider_model_mappings`: Provider 级别的模型映射(最多 100 条)
|
||||
- `provider_id`: Provider ID
|
||||
- `provider_name`: Provider 名称
|
||||
- `global_model_id`: GlobalModel ID
|
||||
- `global_model_name`: GlobalModel 名称
|
||||
- `global_model_display_name`: GlobalModel 显示名称
|
||||
- `provider_model_name`: Provider 侧的模型名称
|
||||
- `aliases`: 别名列表
|
||||
- `ttl`: 缓存剩余 TTL(秒)
|
||||
- `hit_count`: 缓存命中次数
|
||||
- `unmapped`: 未映射或无效的缓存条目
|
||||
"""
|
||||
adapter = AdminModelMappingCacheStatsAdapter()
|
||||
return await pipeline.run(adapter=adapter, http_request=request, db=db, mode=adapter.mode)
|
||||
@@ -1015,7 +1194,15 @@ async def clear_all_model_mapping_cache(
|
||||
"""
|
||||
清除所有模型映射缓存
|
||||
|
||||
警告: 这会影响所有模型解析,请谨慎使用
|
||||
清除系统中所有的模型映射缓存,包括 Model、GlobalModel、
|
||||
模型解析等所有相关缓存。下次请求时将重新从数据库查询。
|
||||
|
||||
**警告**: 此操作会影响所有模型解析,请谨慎使用
|
||||
|
||||
**返回字段**:
|
||||
- `status`: 状态(ok)
|
||||
- `message`: 操作结果消息
|
||||
- `deleted_count`: 删除的缓存键数量
|
||||
"""
|
||||
adapter = AdminClearAllModelMappingCacheAdapter()
|
||||
return await pipeline.run(adapter=adapter, http_request=request, db=db, mode=adapter.mode)
|
||||
@@ -1030,8 +1217,17 @@ async def clear_model_mapping_cache_by_name(
|
||||
"""
|
||||
清除指定模型名称的映射缓存
|
||||
|
||||
参数:
|
||||
- model_name: 模型名称(可以是 GlobalModel.name 或映射名称)
|
||||
根据模型名称清除相关的映射缓存,包括 resolve 缓存和 name 缓存。
|
||||
用于更新单个模型的配置后刷新缓存。
|
||||
|
||||
**路径参数**:
|
||||
- `model_name`: 模型名称(可以是 GlobalModel.name 或映射名称)
|
||||
|
||||
**返回字段**:
|
||||
- `status`: 状态(ok)
|
||||
- `message`: 操作结果消息
|
||||
- `model_name`: 模型名称
|
||||
- `deleted_keys`: 删除的缓存键列表
|
||||
"""
|
||||
adapter = AdminClearModelMappingCacheByNameAdapter(model_name=model_name)
|
||||
return await pipeline.run(adapter=adapter, http_request=request, db=db, mode=adapter.mode)
|
||||
@@ -1047,9 +1243,19 @@ async def clear_provider_model_mapping_cache(
|
||||
"""
|
||||
清除指定 Provider 和 GlobalModel 的模型映射缓存
|
||||
|
||||
参数:
|
||||
- provider_id: Provider ID
|
||||
- global_model_id: GlobalModel ID
|
||||
清除特定 Provider 和 GlobalModel 组合的映射缓存及其命中次数统计。
|
||||
用于 Provider 模型配置更新后刷新缓存。
|
||||
|
||||
**路径参数**:
|
||||
- `provider_id`: Provider ID
|
||||
- `global_model_id`: GlobalModel ID
|
||||
|
||||
**返回字段**:
|
||||
- `status`: 状态(ok)
|
||||
- `message`: 操作结果消息
|
||||
- `provider_id`: Provider ID
|
||||
- `global_model_id`: GlobalModel ID
|
||||
- `deleted_keys`: 删除的缓存键列表
|
||||
"""
|
||||
adapter = AdminClearProviderModelMappingCacheAdapter(
|
||||
provider_id=provider_id, global_model_id=global_model_id
|
||||
|
||||
@@ -71,7 +71,47 @@ async def get_request_trace(
|
||||
request: Request,
|
||||
db: Session = Depends(get_db),
|
||||
):
|
||||
"""获取特定请求的完整追踪信息"""
|
||||
"""
|
||||
获取请求的完整追踪信息
|
||||
|
||||
获取指定请求的完整链路追踪信息,包括所有候选(candidates)的执行情况。
|
||||
|
||||
**路径参数**:
|
||||
- `request_id`: 请求 ID
|
||||
|
||||
**返回字段**:
|
||||
- `request_id`: 请求 ID
|
||||
- `total_candidates`: 候选总数
|
||||
- `final_status`: 最终状态(success: 成功,failed: 失败,streaming: 流式传输中,pending: 等待中)
|
||||
- `total_latency_ms`: 总延迟(毫秒)
|
||||
- `candidates`: 候选列表,每个候选包含:
|
||||
- `id`: 候选 ID
|
||||
- `request_id`: 请求 ID
|
||||
- `candidate_index`: 候选索引
|
||||
- `retry_index`: 重试序号
|
||||
- `provider_id`: 提供商 ID
|
||||
- `provider_name`: 提供商名称
|
||||
- `provider_website`: 提供商官网
|
||||
- `endpoint_id`: 端点 ID
|
||||
- `endpoint_name`: 端点名称(API 格式)
|
||||
- `key_id`: 密钥 ID
|
||||
- `key_name`: 密钥名称
|
||||
- `key_preview`: 密钥脱敏预览
|
||||
- `key_capabilities`: 密钥支持的能力
|
||||
- `required_capabilities`: 请求需要的能力标签
|
||||
- `status`: 状态(pending, success, failed, skipped)
|
||||
- `skip_reason`: 跳过原因
|
||||
- `is_cached`: 是否缓存命中
|
||||
- `status_code`: HTTP 状态码
|
||||
- `error_type`: 错误类型
|
||||
- `error_message`: 错误信息
|
||||
- `latency_ms`: 延迟(毫秒)
|
||||
- `concurrent_requests`: 并发请求数
|
||||
- `extra_data`: 额外数据
|
||||
- `created_at`: 创建时间
|
||||
- `started_at`: 开始时间
|
||||
- `finished_at`: 完成时间
|
||||
"""
|
||||
|
||||
adapter = AdminGetRequestTraceAdapter(request_id=request_id)
|
||||
return await pipeline.run(adapter=adapter, http_request=request, db=db, mode=adapter.mode)
|
||||
@@ -85,9 +125,23 @@ async def get_provider_failure_rate(
|
||||
db: Session = Depends(get_db),
|
||||
):
|
||||
"""
|
||||
获取某个 Provider 的失败率统计
|
||||
获取提供商的失败率统计
|
||||
|
||||
需要管理员权限
|
||||
获取指定提供商最近的失败率统计信息。需要管理员权限。
|
||||
|
||||
**路径参数**:
|
||||
- `provider_id`: 提供商 ID
|
||||
|
||||
**查询参数**:
|
||||
- `limit`: 统计最近的尝试数量,默认 100,最大 1000
|
||||
|
||||
**返回字段**:
|
||||
- `provider_id`: 提供商 ID
|
||||
- `total_attempts`: 总尝试次数
|
||||
- `success_count`: 成功次数
|
||||
- `failed_count`: 失败次数
|
||||
- `failure_rate`: 失败率(百分比)
|
||||
- `avg_latency_ms`: 平均延迟(毫秒)
|
||||
"""
|
||||
adapter = AdminProviderFailureRateAdapter(provider_id=provider_id, limit=limit)
|
||||
return await pipeline.run(adapter=adapter, http_request=request, db=db, mode=adapter.mode)
|
||||
|
||||
@@ -39,6 +39,31 @@ async def update_provider_billing(
|
||||
request: Request,
|
||||
db: Session = Depends(get_db),
|
||||
):
|
||||
"""
|
||||
更新提供商计费配置
|
||||
|
||||
更新指定提供商的计费策略、配额设置和优先级配置。
|
||||
|
||||
**路径参数**:
|
||||
- `provider_id`: 提供商 ID
|
||||
|
||||
**请求体字段**:
|
||||
- `billing_type`: 计费类型(pay_as_you_go、subscription、prepaid、monthly_quota)
|
||||
- `monthly_quota_usd`: 月度配额(美元),可选
|
||||
- `quota_reset_day`: 配额重置周期(天数,1-365),默认 30
|
||||
- `quota_last_reset_at`: 当前周期开始时间,可选(设置后会自动同步该周期内的历史使用量)
|
||||
- `quota_expires_at`: 配额过期时间,可选
|
||||
- `rpm_limit`: 每分钟请求数限制,可选
|
||||
- `provider_priority`: 提供商优先级(0-200),默认 100
|
||||
|
||||
**返回字段**:
|
||||
- `message`: 操作结果信息
|
||||
- `provider`: 更新后的提供商信息
|
||||
- `id`: 提供商 ID
|
||||
- `name`: 提供商名称
|
||||
- `billing_type`: 计费类型
|
||||
- `provider_priority`: 提供商优先级
|
||||
"""
|
||||
adapter = AdminProviderBillingAdapter(provider_id=provider_id)
|
||||
return await pipeline.run(adapter=adapter, http_request=request, db=db, mode=adapter.mode)
|
||||
|
||||
@@ -50,6 +75,39 @@ async def get_provider_stats(
|
||||
hours: int = 24,
|
||||
db: Session = Depends(get_db),
|
||||
):
|
||||
"""
|
||||
获取提供商统计数据
|
||||
|
||||
获取指定提供商的计费信息、RPM 使用情况和使用统计数据。
|
||||
|
||||
**路径参数**:
|
||||
- `provider_id`: 提供商 ID
|
||||
|
||||
**查询参数**:
|
||||
- `hours`: 统计时间范围(小时),默认 24
|
||||
|
||||
**返回字段**:
|
||||
- `provider_id`: 提供商 ID
|
||||
- `provider_name`: 提供商名称
|
||||
- `period_hours`: 统计时间范围
|
||||
- `billing_info`: 计费信息
|
||||
- `billing_type`: 计费类型
|
||||
- `monthly_quota_usd`: 月度配额
|
||||
- `monthly_used_usd`: 月度已使用
|
||||
- `quota_remaining_usd`: 剩余配额
|
||||
- `quota_expires_at`: 配额过期时间
|
||||
- `rpm_info`: RPM 信息
|
||||
- `rpm_limit`: RPM 限制
|
||||
- `rpm_used`: 已使用 RPM
|
||||
- `rpm_reset_at`: RPM 重置时间
|
||||
- `usage_stats`: 使用统计
|
||||
- `total_requests`: 总请求数
|
||||
- `successful_requests`: 成功请求数
|
||||
- `failed_requests`: 失败请求数
|
||||
- `success_rate`: 成功率
|
||||
- `avg_response_time_ms`: 平均响应时间(毫秒)
|
||||
- `total_cost_usd`: 总成本(美元)
|
||||
"""
|
||||
adapter = AdminProviderStatsAdapter(provider_id=provider_id, hours=hours)
|
||||
return await pipeline.run(adapter=adapter, http_request=request, db=db, mode=adapter.mode)
|
||||
|
||||
@@ -67,6 +125,20 @@ async def reset_provider_quota(
|
||||
|
||||
@router.get("/strategies")
|
||||
async def list_available_strategies(request: Request, db: Session = Depends(get_db)):
|
||||
"""
|
||||
获取可用负载均衡策略列表
|
||||
|
||||
列出系统中所有已注册的负载均衡策略插件。
|
||||
|
||||
**返回字段**:
|
||||
- `strategies`: 策略列表
|
||||
- `name`: 策略名称
|
||||
- `priority`: 策略优先级
|
||||
- `version`: 策略版本
|
||||
- `description`: 策略描述
|
||||
- `author`: 策略作者
|
||||
- `total`: 策略总数
|
||||
"""
|
||||
adapter = AdminListStrategiesAdapter()
|
||||
return await pipeline.run(adapter=adapter, http_request=request, db=db, mode=adapter.mode)
|
||||
|
||||
|
||||
@@ -49,7 +49,36 @@ async def list_provider_models(
|
||||
limit: int = 100,
|
||||
db: Session = Depends(get_db),
|
||||
) -> List[ModelResponse]:
|
||||
"""获取提供商的所有模型(管理员)"""
|
||||
"""
|
||||
获取提供商的所有模型
|
||||
|
||||
获取指定提供商的模型列表,支持分页和状态过滤。
|
||||
|
||||
**路径参数**:
|
||||
- `provider_id`: 提供商 ID
|
||||
|
||||
**查询参数**:
|
||||
- `is_active`: 可选的活跃状态过滤,true 仅返回活跃模型,false 返回禁用模型,不传则返回全部
|
||||
- `skip`: 跳过的记录数,默认为 0
|
||||
- `limit`: 返回的最大记录数,默认为 100
|
||||
|
||||
**返回字段**(数组,每项包含):
|
||||
- `id`: 模型 ID
|
||||
- `provider_id`: 提供商 ID
|
||||
- `global_model_id`: 全局模型 ID
|
||||
- `provider_model_name`: 提供商模型名称
|
||||
- `is_active`: 是否启用
|
||||
- `input_price_per_1m`: 输入价格(每百万 token)
|
||||
- `output_price_per_1m`: 输出价格(每百万 token)
|
||||
- `cache_creation_price_per_1m`: 缓存创建价格(每百万 token)
|
||||
- `cache_read_price_per_1m`: 缓存读取价格(每百万 token)
|
||||
- `price_per_request`: 每次请求价格
|
||||
- `supports_vision`: 是否支持视觉
|
||||
- `supports_function_calling`: 是否支持函数调用
|
||||
- `supports_streaming`: 是否支持流式输出
|
||||
- `created_at`: 创建时间
|
||||
- `updated_at`: 更新时间
|
||||
"""
|
||||
adapter = AdminListProviderModelsAdapter(
|
||||
provider_id=provider_id,
|
||||
is_active=is_active,
|
||||
@@ -66,7 +95,29 @@ async def create_provider_model(
|
||||
request: Request,
|
||||
db: Session = Depends(get_db),
|
||||
) -> ModelResponse:
|
||||
"""创建模型(管理员)"""
|
||||
"""
|
||||
创建模型
|
||||
|
||||
为指定提供商创建一个新的模型配置。
|
||||
|
||||
**路径参数**:
|
||||
- `provider_id`: 提供商 ID
|
||||
|
||||
**请求体字段**:
|
||||
- `provider_model_name`: 提供商模型名称(必填)
|
||||
- `global_model_id`: 全局模型 ID(可选,关联到全局模型)
|
||||
- `is_active`: 是否启用(默认 true)
|
||||
- `input_price_per_1m`: 输入价格(每百万 token)(可选)
|
||||
- `output_price_per_1m`: 输出价格(每百万 token)(可选)
|
||||
- `cache_creation_price_per_1m`: 缓存创建价格(每百万 token)(可选)
|
||||
- `cache_read_price_per_1m`: 缓存读取价格(每百万 token)(可选)
|
||||
- `price_per_request`: 每次请求价格(可选)
|
||||
- `supports_vision`: 是否支持视觉(可选)
|
||||
- `supports_function_calling`: 是否支持函数调用(可选)
|
||||
- `supports_streaming`: 是否支持流式输出(可选)
|
||||
|
||||
**返回字段**: 返回创建的模型详细信息(与 GET 单个模型接口返回格式相同)
|
||||
"""
|
||||
adapter = AdminCreateProviderModelAdapter(provider_id=provider_id, model_data=model_data)
|
||||
return await pipeline.run(adapter=adapter, http_request=request, db=db, mode=adapter.mode)
|
||||
|
||||
@@ -78,7 +129,32 @@ async def get_provider_model(
|
||||
request: Request,
|
||||
db: Session = Depends(get_db),
|
||||
) -> ModelResponse:
|
||||
"""获取模型详情(管理员)"""
|
||||
"""
|
||||
获取模型详情
|
||||
|
||||
获取指定模型的详细配置信息。
|
||||
|
||||
**路径参数**:
|
||||
- `provider_id`: 提供商 ID
|
||||
- `model_id`: 模型 ID
|
||||
|
||||
**返回字段**:
|
||||
- `id`: 模型 ID
|
||||
- `provider_id`: 提供商 ID
|
||||
- `global_model_id`: 全局模型 ID
|
||||
- `provider_model_name`: 提供商模型名称
|
||||
- `is_active`: 是否启用
|
||||
- `input_price_per_1m`: 输入价格(每百万 token)
|
||||
- `output_price_per_1m`: 输出价格(每百万 token)
|
||||
- `cache_creation_price_per_1m`: 缓存创建价格(每百万 token)
|
||||
- `cache_read_price_per_1m`: 缓存读取价格(每百万 token)
|
||||
- `price_per_request`: 每次请求价格
|
||||
- `supports_vision`: 是否支持视觉
|
||||
- `supports_function_calling`: 是否支持函数调用
|
||||
- `supports_streaming`: 是否支持流式输出
|
||||
- `created_at`: 创建时间
|
||||
- `updated_at`: 更新时间
|
||||
"""
|
||||
adapter = AdminGetProviderModelAdapter(provider_id=provider_id, model_id=model_id)
|
||||
return await pipeline.run(adapter=adapter, http_request=request, db=db, mode=adapter.mode)
|
||||
|
||||
@@ -91,7 +167,30 @@ async def update_provider_model(
|
||||
request: Request,
|
||||
db: Session = Depends(get_db),
|
||||
) -> ModelResponse:
|
||||
"""更新模型(管理员)"""
|
||||
"""
|
||||
更新模型配置
|
||||
|
||||
更新指定模型的配置信息。只需传入需要更新的字段,未传入的字段保持不变。
|
||||
|
||||
**路径参数**:
|
||||
- `provider_id`: 提供商 ID
|
||||
- `model_id`: 模型 ID
|
||||
|
||||
**请求体字段**(所有字段可选):
|
||||
- `provider_model_name`: 提供商模型名称
|
||||
- `global_model_id`: 全局模型 ID
|
||||
- `is_active`: 是否启用
|
||||
- `input_price_per_1m`: 输入价格(每百万 token)
|
||||
- `output_price_per_1m`: 输出价格(每百万 token)
|
||||
- `cache_creation_price_per_1m`: 缓存创建价格(每百万 token)
|
||||
- `cache_read_price_per_1m`: 缓存读取价格(每百万 token)
|
||||
- `price_per_request`: 每次请求价格
|
||||
- `supports_vision`: 是否支持视觉
|
||||
- `supports_function_calling`: 是否支持函数调用
|
||||
- `supports_streaming`: 是否支持流式输出
|
||||
|
||||
**返回字段**: 返回更新后的模型详细信息(与 GET 单个模型接口返回格式相同)
|
||||
"""
|
||||
adapter = AdminUpdateProviderModelAdapter(
|
||||
provider_id=provider_id,
|
||||
model_id=model_id,
|
||||
@@ -107,7 +206,18 @@ async def delete_provider_model(
|
||||
request: Request,
|
||||
db: Session = Depends(get_db),
|
||||
):
|
||||
"""删除模型(管理员)"""
|
||||
"""
|
||||
删除模型
|
||||
|
||||
删除指定的模型配置。注意:此操作不可逆。
|
||||
|
||||
**路径参数**:
|
||||
- `provider_id`: 提供商 ID
|
||||
- `model_id`: 模型 ID
|
||||
|
||||
**返回字段**:
|
||||
- `message`: 删除成功提示信息
|
||||
"""
|
||||
adapter = AdminDeleteProviderModelAdapter(provider_id=provider_id, model_id=model_id)
|
||||
return await pipeline.run(adapter=adapter, http_request=request, db=db, mode=adapter.mode)
|
||||
|
||||
@@ -119,7 +229,29 @@ async def batch_create_provider_models(
|
||||
request: Request,
|
||||
db: Session = Depends(get_db),
|
||||
) -> List[ModelResponse]:
|
||||
"""批量创建模型(管理员)"""
|
||||
"""
|
||||
批量创建模型
|
||||
|
||||
为指定提供商批量创建多个模型配置。
|
||||
|
||||
**路径参数**:
|
||||
- `provider_id`: 提供商 ID
|
||||
|
||||
**请求体**: 模型数据数组,每项包含:
|
||||
- `provider_model_name`: 提供商模型名称(必填)
|
||||
- `global_model_id`: 全局模型 ID(可选)
|
||||
- `is_active`: 是否启用(默认 true)
|
||||
- `input_price_per_1m`: 输入价格(每百万 token)(可选)
|
||||
- `output_price_per_1m`: 输出价格(每百万 token)(可选)
|
||||
- `cache_creation_price_per_1m`: 缓存创建价格(每百万 token)(可选)
|
||||
- `cache_read_price_per_1m`: 缓存读取价格(每百万 token)(可选)
|
||||
- `price_per_request`: 每次请求价格(可选)
|
||||
- `supports_vision`: 是否支持视觉(可选)
|
||||
- `supports_function_calling`: 是否支持函数调用(可选)
|
||||
- `supports_streaming`: 是否支持流式输出(可选)
|
||||
|
||||
**返回字段**: 返回创建的模型列表(与 GET 模型列表接口返回格式相同)
|
||||
"""
|
||||
adapter = AdminBatchCreateModelsAdapter(provider_id=provider_id, models_data=models_data)
|
||||
return await pipeline.run(adapter=adapter, http_request=request, db=db, mode=adapter.mode)
|
||||
|
||||
@@ -134,10 +266,23 @@ async def get_provider_available_source_models(
|
||||
db: Session = Depends(get_db),
|
||||
):
|
||||
"""
|
||||
获取该 Provider 支持的所有统一模型名(source_model)
|
||||
获取提供商支持的可用源模型
|
||||
|
||||
包括:
|
||||
1. 直连模型(Model.provider_model_name 直接作为统一模型名)
|
||||
获取该提供商支持的所有统一模型名(source_model),包含价格和能力信息。
|
||||
|
||||
**路径参数**:
|
||||
- `provider_id`: 提供商 ID
|
||||
|
||||
**返回字段**:
|
||||
- `models`: 可用源模型数组,每项包含:
|
||||
- `global_model_name`: 全局模型名称
|
||||
- `display_name`: 显示名称
|
||||
- `provider_model_name`: 提供商模型名称
|
||||
- `model_id`: 模型 ID
|
||||
- `price`: 价格信息(包含 input_price_per_1m, output_price_per_1m, cache_creation_price_per_1m, cache_read_price_per_1m, price_per_request)
|
||||
- `capabilities`: 能力信息(包含 supports_vision, supports_function_calling, supports_streaming)
|
||||
- `is_active`: 是否启用
|
||||
- `total`: 总数
|
||||
"""
|
||||
adapter = AdminGetProviderAvailableSourceModelsAdapter(provider_id=provider_id)
|
||||
return await pipeline.run(adapter=adapter, http_request=request, db=db, mode=adapter.mode)
|
||||
@@ -153,7 +298,27 @@ async def batch_assign_global_models_to_provider(
|
||||
request: Request,
|
||||
db: Session = Depends(get_db),
|
||||
) -> BatchAssignModelsToProviderResponse:
|
||||
"""批量为 Provider 关联 GlobalModels(自动继承价格和能力配置)"""
|
||||
"""
|
||||
批量关联全局模型
|
||||
|
||||
批量为提供商关联全局模型,自动继承全局模型的价格和能力配置。
|
||||
|
||||
**路径参数**:
|
||||
- `provider_id`: 提供商 ID
|
||||
|
||||
**请求体字段**:
|
||||
- `global_model_ids`: 全局模型 ID 数组(必填)
|
||||
|
||||
**返回字段**:
|
||||
- `success`: 成功关联的模型数组,每项包含:
|
||||
- `global_model_id`: 全局模型 ID
|
||||
- `global_model_name`: 全局模型名称
|
||||
- `model_id`: 新创建的模型 ID
|
||||
- `errors`: 失败的模型数组,每项包含:
|
||||
- `global_model_id`: 全局模型 ID
|
||||
- `global_model_name`: 全局模型名称(如果可用)
|
||||
- `error`: 错误信息
|
||||
"""
|
||||
adapter = AdminBatchAssignModelsToProviderAdapter(
|
||||
provider_id=provider_id, payload=payload
|
||||
)
|
||||
@@ -173,10 +338,30 @@ async def import_models_from_upstream(
|
||||
"""
|
||||
从上游提供商导入模型
|
||||
|
||||
流程:
|
||||
从上游提供商导入模型列表。如果全局模型不存在,将自动创建。
|
||||
|
||||
**流程说明**:
|
||||
1. 根据 model_ids 检查全局模型是否存在(按 name 匹配)
|
||||
2. 如不存在,自动创建新的 GlobalModel(使用默认配置)
|
||||
2. 如不存在,自动创建新的 GlobalModel(使用默认免费配置)
|
||||
3. 创建 Model 关联到当前 Provider
|
||||
4. 如模型已关联,则记录到成功列表中
|
||||
|
||||
**路径参数**:
|
||||
- `provider_id`: 提供商 ID
|
||||
|
||||
**请求体字段**:
|
||||
- `model_ids`: 模型 ID 数组(必填,每个 ID 长度 1-100 字符)
|
||||
|
||||
**返回字段**:
|
||||
- `success`: 成功导入的模型数组,每项包含:
|
||||
- `model_id`: 模型 ID
|
||||
- `global_model_id`: 全局模型 ID
|
||||
- `global_model_name`: 全局模型名称
|
||||
- `provider_model_id`: 提供商模型 ID
|
||||
- `created_global_model`: 是否新创建了全局模型
|
||||
- `errors`: 失败的模型数组,每项包含:
|
||||
- `model_id`: 模型 ID
|
||||
- `error`: 错误信息
|
||||
"""
|
||||
adapter = AdminImportFromUpstreamAdapter(provider_id=provider_id, payload=payload)
|
||||
return await pipeline.run(adapter=adapter, http_request=request, db=db, mode=adapter.mode)
|
||||
|
||||
@@ -27,24 +27,116 @@ async def list_providers(
|
||||
is_active: Optional[bool] = None,
|
||||
db: Session = Depends(get_db),
|
||||
):
|
||||
"""
|
||||
获取提供商列表
|
||||
|
||||
获取所有提供商的基本信息列表,支持分页和状态过滤。
|
||||
|
||||
**查询参数**:
|
||||
- `skip`: 跳过的记录数,用于分页,默认为 0
|
||||
- `limit`: 返回的最大记录数,范围 1-500,默认为 100
|
||||
- `is_active`: 可选的活跃状态过滤,true 仅返回活跃提供商,false 返回禁用提供商,不传则返回全部
|
||||
|
||||
**返回字段**:
|
||||
- `id`: 提供商 ID
|
||||
- `name`: 提供商名称(唯一标识)
|
||||
- `display_name`: 显示名称
|
||||
- `api_format`: API 格式(如 claude、openai、gemini 等)
|
||||
- `base_url`: API 基础 URL
|
||||
- `api_key`: API 密钥(脱敏显示)
|
||||
- `priority`: 优先级
|
||||
- `is_active`: 是否活跃
|
||||
- `created_at`: 创建时间
|
||||
- `updated_at`: 更新时间
|
||||
"""
|
||||
adapter = AdminListProvidersAdapter(skip=skip, limit=limit, is_active=is_active)
|
||||
return await pipeline.run(adapter=adapter, http_request=request, db=db, mode=adapter.mode)
|
||||
|
||||
|
||||
@router.post("/")
|
||||
async def create_provider(request: Request, db: Session = Depends(get_db)):
|
||||
"""
|
||||
创建新提供商
|
||||
|
||||
创建一个新的 AI 模型提供商配置。
|
||||
|
||||
**请求体字段**:
|
||||
- `name`: 提供商名称(必填,唯一,用于系统标识)
|
||||
- `display_name`: 显示名称(必填)
|
||||
- `description`: 描述信息(可选)
|
||||
- `website`: 官网地址(可选)
|
||||
- `billing_type`: 计费类型(可选,pay_as_you_go/subscription/prepaid,默认 pay_as_you_go)
|
||||
- `monthly_quota_usd`: 月度配额(美元)(可选)
|
||||
- `quota_reset_day`: 配额重置日期(1-31)(可选)
|
||||
- `quota_last_reset_at`: 上次配额重置时间(可选)
|
||||
- `quota_expires_at`: 配额过期时间(可选)
|
||||
- `rpm_limit`: 每分钟请求数限制(可选)
|
||||
- `provider_priority`: 提供商优先级(数字越小优先级越高,默认 100)
|
||||
- `is_active`: 是否启用(默认 true)
|
||||
- `rate_limit`: 速率限制配置(可选)
|
||||
- `concurrent_limit`: 并发限制(可选)
|
||||
- `config`: 额外配置信息(JSON,可选)
|
||||
|
||||
**返回字段**:
|
||||
- `id`: 新创建的提供商 ID
|
||||
- `name`: 提供商名称
|
||||
- `display_name`: 显示名称
|
||||
- `message`: 成功提示信息
|
||||
"""
|
||||
adapter = AdminCreateProviderAdapter()
|
||||
return await pipeline.run(adapter=adapter, http_request=request, db=db, mode=adapter.mode)
|
||||
|
||||
|
||||
@router.put("/{provider_id}")
|
||||
async def update_provider(provider_id: str, request: Request, db: Session = Depends(get_db)):
|
||||
"""
|
||||
更新提供商配置
|
||||
|
||||
更新指定提供商的配置信息。只需传入需要更新的字段,未传入的字段保持不变。
|
||||
|
||||
**路径参数**:
|
||||
- `provider_id`: 提供商 ID
|
||||
|
||||
**请求体字段**(所有字段可选):
|
||||
- `name`: 提供商名称
|
||||
- `display_name`: 显示名称
|
||||
- `description`: 描述信息
|
||||
- `website`: 官网地址
|
||||
- `billing_type`: 计费类型(pay_as_you_go/subscription/prepaid)
|
||||
- `monthly_quota_usd`: 月度配额(美元)
|
||||
- `quota_reset_day`: 配额重置日期(1-31)
|
||||
- `quota_last_reset_at`: 上次配额重置时间
|
||||
- `quota_expires_at`: 配额过期时间
|
||||
- `rpm_limit`: 每分钟请求数限制
|
||||
- `provider_priority`: 提供商优先级
|
||||
- `is_active`: 是否启用
|
||||
- `rate_limit`: 速率限制配置
|
||||
- `concurrent_limit`: 并发限制
|
||||
- `config`: 额外配置信息(JSON)
|
||||
|
||||
**返回字段**:
|
||||
- `id`: 提供商 ID
|
||||
- `name`: 提供商名称
|
||||
- `is_active`: 是否启用
|
||||
- `message`: 成功提示信息
|
||||
"""
|
||||
adapter = AdminUpdateProviderAdapter(provider_id=provider_id)
|
||||
return await pipeline.run(adapter=adapter, http_request=request, db=db, mode=adapter.mode)
|
||||
|
||||
|
||||
@router.delete("/{provider_id}")
|
||||
async def delete_provider(provider_id: str, request: Request, db: Session = Depends(get_db)):
|
||||
"""
|
||||
删除提供商
|
||||
|
||||
删除指定的提供商。注意:此操作会级联删除关联的端点、密钥和模型配置。
|
||||
|
||||
**路径参数**:
|
||||
- `provider_id`: 提供商 ID
|
||||
|
||||
**返回字段**:
|
||||
- `message`: 删除成功提示信息
|
||||
"""
|
||||
adapter = AdminDeleteProviderAdapter(provider_id=provider_id)
|
||||
return await pipeline.run(adapter=adapter, http_request=request, db=db, mode=adapter.mode)
|
||||
|
||||
|
||||
@@ -40,7 +40,41 @@ async def get_providers_summary(
|
||||
request: Request,
|
||||
db: Session = Depends(get_db),
|
||||
) -> List[ProviderWithEndpointsSummary]:
|
||||
"""获取所有 Providers 的摘要信息(包含 Endpoints 和 Keys 统计)"""
|
||||
"""
|
||||
获取所有提供商摘要信息
|
||||
|
||||
获取所有提供商的详细摘要信息,包含端点、密钥、模型统计和健康状态。
|
||||
|
||||
**返回字段**(数组,每项包含):
|
||||
- `id`: 提供商 ID
|
||||
- `name`: 提供商名称
|
||||
- `display_name`: 显示名称
|
||||
- `description`: 描述信息
|
||||
- `website`: 官网地址
|
||||
- `provider_priority`: 优先级
|
||||
- `is_active`: 是否启用
|
||||
- `billing_type`: 计费类型
|
||||
- `monthly_quota_usd`: 月度配额(美元)
|
||||
- `monthly_used_usd`: 本月已使用金额(美元)
|
||||
- `quota_reset_day`: 配额重置日期
|
||||
- `quota_last_reset_at`: 上次配额重置时间
|
||||
- `quota_expires_at`: 配额过期时间
|
||||
- `rpm_limit`: RPM 限制
|
||||
- `rpm_used`: 已使用 RPM
|
||||
- `rpm_reset_at`: RPM 重置时间
|
||||
- `total_endpoints`: 端点总数
|
||||
- `active_endpoints`: 活跃端点数
|
||||
- `total_keys`: 密钥总数
|
||||
- `active_keys`: 活跃密钥数
|
||||
- `total_models`: 模型总数
|
||||
- `active_models`: 活跃模型数
|
||||
- `avg_health_score`: 平均健康分数(0-1)
|
||||
- `unhealthy_endpoints`: 不健康端点数(健康分数 < 0.5)
|
||||
- `api_formats`: 支持的 API 格式列表
|
||||
- `endpoint_health_details`: 端点健康详情(包含 api_format, health_score, is_active, active_keys)
|
||||
- `created_at`: 创建时间
|
||||
- `updated_at`: 更新时间
|
||||
"""
|
||||
adapter = AdminProviderSummaryAdapter()
|
||||
return await pipeline.run(adapter=adapter, http_request=request, db=db, mode=adapter.mode)
|
||||
|
||||
@@ -51,7 +85,44 @@ async def get_provider_summary(
|
||||
request: Request,
|
||||
db: Session = Depends(get_db),
|
||||
) -> ProviderWithEndpointsSummary:
|
||||
"""获取单个 Provider 的摘要信息(包含 Endpoints 和 Keys 统计)"""
|
||||
"""
|
||||
获取单个提供商摘要信息
|
||||
|
||||
获取指定提供商的详细摘要信息,包含端点、密钥、模型统计和健康状态。
|
||||
|
||||
**路径参数**:
|
||||
- `provider_id`: 提供商 ID
|
||||
|
||||
**返回字段**:
|
||||
- `id`: 提供商 ID
|
||||
- `name`: 提供商名称
|
||||
- `display_name`: 显示名称
|
||||
- `description`: 描述信息
|
||||
- `website`: 官网地址
|
||||
- `provider_priority`: 优先级
|
||||
- `is_active`: 是否启用
|
||||
- `billing_type`: 计费类型
|
||||
- `monthly_quota_usd`: 月度配额(美元)
|
||||
- `monthly_used_usd`: 本月已使用金额(美元)
|
||||
- `quota_reset_day`: 配额重置日期
|
||||
- `quota_last_reset_at`: 上次配额重置时间
|
||||
- `quota_expires_at`: 配额过期时间
|
||||
- `rpm_limit`: RPM 限制
|
||||
- `rpm_used`: 已使用 RPM
|
||||
- `rpm_reset_at`: RPM 重置时间
|
||||
- `total_endpoints`: 端点总数
|
||||
- `active_endpoints`: 活跃端点数
|
||||
- `total_keys`: 密钥总数
|
||||
- `active_keys`: 活跃密钥数
|
||||
- `total_models`: 模型总数
|
||||
- `active_models`: 活跃模型数
|
||||
- `avg_health_score`: 平均健康分数(0-1)
|
||||
- `unhealthy_endpoints`: 不健康端点数(健康分数 < 0.5)
|
||||
- `api_formats`: 支持的 API 格式列表
|
||||
- `endpoint_health_details`: 端点健康详情(包含 api_format, health_score, is_active, active_keys)
|
||||
- `created_at`: 创建时间
|
||||
- `updated_at`: 更新时间
|
||||
"""
|
||||
provider = db.query(Provider).filter(Provider.id == provider_id).first()
|
||||
if not provider:
|
||||
raise NotFoundException(f"Provider {provider_id} not found")
|
||||
@@ -67,7 +138,34 @@ async def get_provider_health_monitor(
|
||||
per_endpoint_limit: int = Query(48, ge=10, le=200, description="每个端点的事件数量"),
|
||||
db: Session = Depends(get_db),
|
||||
) -> ProviderEndpointHealthMonitorResponse:
|
||||
"""获取 Provider 下所有端点的健康监控时间线"""
|
||||
"""
|
||||
获取提供商健康监控数据
|
||||
|
||||
获取指定提供商下所有端点的健康监控时间线,包含请求成功率、延迟、错误信息等。
|
||||
|
||||
**路径参数**:
|
||||
- `provider_id`: 提供商 ID
|
||||
|
||||
**查询参数**:
|
||||
- `lookback_hours`: 回溯的小时数,范围 1-72,默认为 6
|
||||
- `per_endpoint_limit`: 每个端点返回的事件数量,范围 10-200,默认为 48
|
||||
|
||||
**返回字段**:
|
||||
- `provider_id`: 提供商 ID
|
||||
- `provider_name`: 提供商名称
|
||||
- `generated_at`: 生成时间
|
||||
- `endpoints`: 端点健康监控数据数组,每项包含:
|
||||
- `endpoint_id`: 端点 ID
|
||||
- `api_format`: API 格式
|
||||
- `is_active`: 是否活跃
|
||||
- `total_attempts`: 总请求次数
|
||||
- `success_count`: 成功次数
|
||||
- `failed_count`: 失败次数
|
||||
- `skipped_count`: 跳过次数
|
||||
- `success_rate`: 成功率(0-1)
|
||||
- `last_event_at`: 最后事件时间
|
||||
- `events`: 事件详情数组(包含 timestamp, status, status_code, latency_ms, error_type, error_message)
|
||||
"""
|
||||
|
||||
adapter = AdminProviderHealthMonitorAdapter(
|
||||
provider_id=provider_id,
|
||||
@@ -84,7 +182,29 @@ async def update_provider_settings(
|
||||
request: Request,
|
||||
db: Session = Depends(get_db),
|
||||
) -> ProviderWithEndpointsSummary:
|
||||
"""更新 Provider 基础配置(display_name, description, priority, weight 等)"""
|
||||
"""
|
||||
更新提供商基础配置
|
||||
|
||||
更新提供商的基础配置信息,如显示名称、描述、优先级等。只需传入需要更新的字段。
|
||||
|
||||
**路径参数**:
|
||||
- `provider_id`: 提供商 ID
|
||||
|
||||
**请求体字段**(所有字段可选):
|
||||
- `display_name`: 显示名称
|
||||
- `description`: 描述信息
|
||||
- `website`: 官网地址
|
||||
- `provider_priority`: 优先级
|
||||
- `is_active`: 是否启用
|
||||
- `billing_type`: 计费类型
|
||||
- `monthly_quota_usd`: 月度配额(美元)
|
||||
- `quota_reset_day`: 配额重置日期
|
||||
- `quota_last_reset_at`: 上次配额重置时间
|
||||
- `quota_expires_at`: 配额过期时间
|
||||
- `rpm_limit`: RPM 限制
|
||||
|
||||
**返回字段**: 返回更新后的提供商摘要信息(与 GET /summary 接口返回格式相同)
|
||||
"""
|
||||
|
||||
adapter = AdminUpdateProviderSettingsAdapter(provider_id=provider_id, update_data=update_data)
|
||||
return await pipeline.run(adapter=adapter, http_request=request, db=db, mode=adapter.mode)
|
||||
|
||||
@@ -18,7 +18,7 @@ from src.core.logger import logger
|
||||
from src.database import get_db
|
||||
from src.services.rate_limit.ip_limiter import IPRateLimiter
|
||||
|
||||
router = APIRouter(prefix="/api/admin/security/ip", tags=["IP Security"])
|
||||
router = APIRouter(prefix="/api/admin/security/ip", tags=["Admin - Security"])
|
||||
pipeline = ApiRequestPipeline()
|
||||
|
||||
|
||||
@@ -56,42 +56,110 @@ class RemoveIPFromWhitelistRequest(BaseModel):
|
||||
|
||||
@router.post("/blacklist")
|
||||
async def add_to_blacklist(request: Request, db: Session = Depends(get_db)):
|
||||
"""Add IP to blacklist"""
|
||||
"""
|
||||
添加 IP 到黑名单
|
||||
|
||||
将指定 IP 地址添加到黑名单,被加入黑名单的 IP 将无法访问系统。需要管理员权限。
|
||||
|
||||
**请求体字段**:
|
||||
- `ip_address`: IP 地址
|
||||
- `reason`: 加入黑名单的原因
|
||||
- `ttl`: 可选,过期时间(秒),不指定表示永久
|
||||
|
||||
**返回字段**:
|
||||
- `success`: 是否成功
|
||||
- `message`: 操作结果信息
|
||||
- `reason`: 加入黑名单的原因
|
||||
- `ttl`: 过期时间(秒或"永久")
|
||||
"""
|
||||
adapter = AddToBlacklistAdapter()
|
||||
return await pipeline.run(adapter=adapter, http_request=request, db=db, mode=ApiMode.ADMIN)
|
||||
|
||||
|
||||
@router.delete("/blacklist/{ip_address}")
|
||||
async def remove_from_blacklist(ip_address: str, request: Request, db: Session = Depends(get_db)):
|
||||
"""Remove IP from blacklist"""
|
||||
"""
|
||||
从黑名单移除 IP
|
||||
|
||||
将指定 IP 地址从黑名单中移除。需要管理员权限。
|
||||
|
||||
**路径参数**:
|
||||
- `ip_address`: IP 地址
|
||||
|
||||
**返回字段**:
|
||||
- `success`: 是否成功
|
||||
- `message`: 操作结果信息
|
||||
"""
|
||||
adapter = RemoveFromBlacklistAdapter(ip_address=ip_address)
|
||||
return await pipeline.run(adapter=adapter, http_request=request, db=db, mode=ApiMode.ADMIN)
|
||||
|
||||
|
||||
@router.get("/blacklist/stats")
|
||||
async def get_blacklist_stats(request: Request, db: Session = Depends(get_db)):
|
||||
"""Get blacklist statistics"""
|
||||
"""
|
||||
获取黑名单统计信息
|
||||
|
||||
获取黑名单的统计信息和列表。需要管理员权限。
|
||||
|
||||
**返回字段**:
|
||||
- `total`: 黑名单总数
|
||||
- `items`: 黑名单列表,每个项包含:
|
||||
- `ip`: IP 地址
|
||||
- `reason`: 加入原因
|
||||
- `added_at`: 添加时间
|
||||
- `ttl`: 剩余有效时间(秒)
|
||||
"""
|
||||
adapter = GetBlacklistStatsAdapter()
|
||||
return await pipeline.run(adapter=adapter, http_request=request, db=db, mode=ApiMode.ADMIN)
|
||||
|
||||
|
||||
@router.post("/whitelist")
|
||||
async def add_to_whitelist(request: Request, db: Session = Depends(get_db)):
|
||||
"""Add IP to whitelist"""
|
||||
"""
|
||||
添加 IP 到白名单
|
||||
|
||||
将指定 IP 地址或 CIDR 网段添加到白名单,白名单中的 IP 将跳过速率限制检查。需要管理员权限。
|
||||
|
||||
**请求体字段**:
|
||||
- `ip_address`: IP 地址或 CIDR 格式(如 192.168.1.0/24)
|
||||
|
||||
**返回字段**:
|
||||
- `success`: 是否成功
|
||||
- `message`: 操作结果信息
|
||||
"""
|
||||
adapter = AddToWhitelistAdapter()
|
||||
return await pipeline.run(adapter=adapter, http_request=request, db=db, mode=ApiMode.ADMIN)
|
||||
|
||||
|
||||
@router.delete("/whitelist/{ip_address}")
|
||||
async def remove_from_whitelist(ip_address: str, request: Request, db: Session = Depends(get_db)):
|
||||
"""Remove IP from whitelist"""
|
||||
"""
|
||||
从白名单移除 IP
|
||||
|
||||
将指定 IP 地址从白名单中移除。需要管理员权限。
|
||||
|
||||
**路径参数**:
|
||||
- `ip_address`: IP 地址
|
||||
|
||||
**返回字段**:
|
||||
- `success`: 是否成功
|
||||
- `message`: 操作结果信息
|
||||
"""
|
||||
adapter = RemoveFromWhitelistAdapter(ip_address=ip_address)
|
||||
return await pipeline.run(adapter=adapter, http_request=request, db=db, mode=ApiMode.ADMIN)
|
||||
|
||||
|
||||
@router.get("/whitelist")
|
||||
async def get_whitelist(request: Request, db: Session = Depends(get_db)):
|
||||
"""Get whitelist"""
|
||||
"""
|
||||
获取白名单
|
||||
|
||||
获取当前的 IP 白名单列表。需要管理员权限。
|
||||
|
||||
**返回字段**:
|
||||
- `whitelist`: 白名单 IP 地址列表
|
||||
- `total`: 白名单总数
|
||||
"""
|
||||
adapter = GetWhitelistAdapter()
|
||||
return await pipeline.run(adapter=adapter, http_request=request, db=db, mode=ApiMode.ADMIN)
|
||||
|
||||
|
||||
@@ -44,7 +44,14 @@ def _get_version_from_git() -> str | None:
|
||||
|
||||
@router.get("/version")
|
||||
async def get_system_version():
|
||||
"""获取系统版本信息"""
|
||||
"""
|
||||
获取系统版本信息
|
||||
|
||||
获取当前系统的版本号。优先从 git describe 获取,回退到静态版本文件。
|
||||
|
||||
**返回字段**:
|
||||
- `version`: 版本号字符串
|
||||
"""
|
||||
# 优先从 git 获取
|
||||
version = _get_version_from_git()
|
||||
if version:
|
||||
@@ -64,7 +71,16 @@ pipeline = ApiRequestPipeline()
|
||||
|
||||
@router.get("/settings")
|
||||
async def get_system_settings(request: Request, db: Session = Depends(get_db)):
|
||||
"""获取系统设置(管理员)"""
|
||||
"""
|
||||
获取系统设置
|
||||
|
||||
获取系统的全局设置信息。需要管理员权限。
|
||||
|
||||
**返回字段**:
|
||||
- `default_provider`: 默认提供商名称
|
||||
- `default_model`: 默认模型名称
|
||||
- `enable_usage_tracking`: 是否启用使用情况追踪
|
||||
"""
|
||||
|
||||
adapter = AdminGetSystemSettingsAdapter()
|
||||
return await pipeline.run(adapter=adapter, http_request=request, db=db, mode=adapter.mode)
|
||||
@@ -72,7 +88,19 @@ async def get_system_settings(request: Request, db: Session = Depends(get_db)):
|
||||
|
||||
@router.put("/settings")
|
||||
async def update_system_settings(http_request: Request, db: Session = Depends(get_db)):
|
||||
"""更新系统设置(管理员)"""
|
||||
"""
|
||||
更新系统设置
|
||||
|
||||
更新系统的全局设置。需要管理员权限。
|
||||
|
||||
**请求体字段**:
|
||||
- `default_provider`: 可选,默认提供商名称(空字符串表示清除设置)
|
||||
- `default_model`: 可选,默认模型名称(空字符串表示清除设置)
|
||||
- `enable_usage_tracking`: 可选,是否启用使用情况追踪
|
||||
|
||||
**返回字段**:
|
||||
- `message`: 操作结果信息
|
||||
"""
|
||||
|
||||
adapter = AdminUpdateSystemSettingsAdapter()
|
||||
return await pipeline.run(adapter=adapter, http_request=http_request, db=db, mode=adapter.mode)
|
||||
@@ -80,7 +108,14 @@ async def update_system_settings(http_request: Request, db: Session = Depends(ge
|
||||
|
||||
@router.get("/configs")
|
||||
async def get_all_system_configs(request: Request, db: Session = Depends(get_db)):
|
||||
"""获取所有系统配置(管理员)"""
|
||||
"""
|
||||
获取所有系统配置
|
||||
|
||||
获取系统中所有的配置项。需要管理员权限。
|
||||
|
||||
**返回字段**:
|
||||
- 配置项的键值对字典
|
||||
"""
|
||||
|
||||
adapter = AdminGetAllConfigsAdapter()
|
||||
return await pipeline.run(adapter=adapter, http_request=request, db=db, mode=adapter.mode)
|
||||
@@ -88,7 +123,19 @@ async def get_all_system_configs(request: Request, db: Session = Depends(get_db)
|
||||
|
||||
@router.get("/configs/{key}")
|
||||
async def get_system_config(key: str, request: Request, db: Session = Depends(get_db)):
|
||||
"""获取特定系统配置(管理员)"""
|
||||
"""
|
||||
获取特定系统配置
|
||||
|
||||
获取指定配置项的值。需要管理员权限。
|
||||
|
||||
**路径参数**:
|
||||
- `key`: 配置项键名
|
||||
|
||||
**返回字段**:
|
||||
- `key`: 配置项键名
|
||||
- `value`: 配置项的值(敏感配置项不返回实际值)
|
||||
- `is_set`: 可选,对于敏感配置项,指示是否已设置
|
||||
"""
|
||||
|
||||
adapter = AdminGetSystemConfigAdapter(key=key)
|
||||
return await pipeline.run(adapter=adapter, http_request=request, db=db, mode=adapter.mode)
|
||||
@@ -100,7 +147,24 @@ async def set_system_config(
|
||||
request: Request,
|
||||
db: Session = Depends(get_db),
|
||||
):
|
||||
"""设置系统配置(管理员)"""
|
||||
"""
|
||||
设置系统配置
|
||||
|
||||
设置或更新指定配置项的值。需要管理员权限。
|
||||
|
||||
**路径参数**:
|
||||
- `key`: 配置项键名
|
||||
|
||||
**请求体字段**:
|
||||
- `value`: 配置项的值
|
||||
- `description`: 可选,配置项描述
|
||||
|
||||
**返回字段**:
|
||||
- `key`: 配置项键名
|
||||
- `value`: 配置项的值(敏感配置项显示为 ********)
|
||||
- `description`: 配置项描述
|
||||
- `updated_at`: 更新时间
|
||||
"""
|
||||
|
||||
adapter = AdminSetSystemConfigAdapter(key=key)
|
||||
return await pipeline.run(adapter=adapter, http_request=request, db=db, mode=adapter.mode)
|
||||
@@ -108,7 +172,17 @@ async def set_system_config(
|
||||
|
||||
@router.delete("/configs/{key}")
|
||||
async def delete_system_config(key: str, request: Request, db: Session = Depends(get_db)):
|
||||
"""删除系统配置(管理员)"""
|
||||
"""
|
||||
删除系统配置
|
||||
|
||||
删除指定的配置项。需要管理员权限。
|
||||
|
||||
**路径参数**:
|
||||
- `key`: 配置项键名
|
||||
|
||||
**返回字段**:
|
||||
- `message`: 操作结果信息
|
||||
"""
|
||||
|
||||
adapter = AdminDeleteSystemConfigAdapter(key=key)
|
||||
return await pipeline.run(adapter=adapter, http_request=request, db=db, mode=adapter.mode)
|
||||
@@ -116,20 +190,54 @@ async def delete_system_config(key: str, request: Request, db: Session = Depends
|
||||
|
||||
@router.get("/stats")
|
||||
async def get_system_stats(request: Request, db: Session = Depends(get_db)):
|
||||
"""
|
||||
获取系统统计信息
|
||||
|
||||
获取系统的整体统计数据。需要管理员权限。
|
||||
|
||||
**返回字段**:
|
||||
- `users`: 用户统计(total: 总用户数, active: 活跃用户数)
|
||||
- `providers`: 提供商统计(total: 总提供商数, active: 活跃提供商数)
|
||||
- `api_keys`: API Key 总数
|
||||
- `requests`: 请求总数
|
||||
"""
|
||||
adapter = AdminSystemStatsAdapter()
|
||||
return await pipeline.run(adapter=adapter, http_request=request, db=db, mode=adapter.mode)
|
||||
|
||||
|
||||
@router.post("/cleanup")
|
||||
async def trigger_cleanup(request: Request, db: Session = Depends(get_db)):
|
||||
"""Manually trigger usage record cleanup task"""
|
||||
"""
|
||||
手动触发清理任务
|
||||
|
||||
手动触发使用记录清理任务,清理过期的请求/响应数据。需要管理员权限。
|
||||
|
||||
**返回字段**:
|
||||
- `message`: 操作结果信息
|
||||
- `stats`: 清理统计信息
|
||||
- `total_records`: 总记录数统计(before, after, deleted)
|
||||
- `body_fields`: 请求/响应体字段清理统计(before, after, cleaned)
|
||||
- `header_fields`: 请求/响应头字段清理统计(before, after, cleaned)
|
||||
- `timestamp`: 清理完成时间
|
||||
"""
|
||||
adapter = AdminTriggerCleanupAdapter()
|
||||
return await pipeline.run(adapter=adapter, http_request=request, db=db, mode=adapter.mode)
|
||||
|
||||
|
||||
@router.get("/api-formats")
|
||||
async def get_api_formats(request: Request, db: Session = Depends(get_db)):
|
||||
"""获取所有可用的API格式列表"""
|
||||
"""
|
||||
获取所有可用的 API 格式列表
|
||||
|
||||
获取系统支持的所有 API 格式及其元数据。需要管理员权限。
|
||||
|
||||
**返回字段**:
|
||||
- `formats`: API 格式列表,每个格式包含:
|
||||
- `value`: 格式值
|
||||
- `label`: 显示名称
|
||||
- `default_path`: 默认路径
|
||||
- `aliases`: 别名列表
|
||||
"""
|
||||
adapter = AdminGetApiFormatsAdapter()
|
||||
return await pipeline.run(adapter=adapter, http_request=request, db=db, mode=adapter.mode)
|
||||
|
||||
|
||||
@@ -39,12 +39,21 @@ async def get_usage_aggregation(
|
||||
db: Session = Depends(get_db),
|
||||
):
|
||||
"""
|
||||
Get usage aggregation by specified dimension.
|
||||
获取使用情况聚合统计
|
||||
|
||||
- group_by=model: Aggregate by model
|
||||
- group_by=user: Aggregate by user
|
||||
- group_by=provider: Aggregate by provider
|
||||
- group_by=api_format: Aggregate by API format
|
||||
按指定维度聚合使用情况统计数据。
|
||||
|
||||
**查询参数**:
|
||||
- `group_by`: 必需,聚合维度,可选值:model(按模型)、user(按用户)、provider(按提供商)、api_format(按 API 格式)
|
||||
- `start_date`: 可选,开始日期(ISO 格式)
|
||||
- `end_date`: 可选,结束日期(ISO 格式)
|
||||
- `limit`: 返回数量限制,默认 20,最大 100
|
||||
|
||||
**返回字段**:
|
||||
- 按模型聚合时:model, request_count, total_tokens, total_cost, actual_cost
|
||||
- 按用户聚合时:user_id, email, username, request_count, total_tokens, total_cost
|
||||
- 按提供商聚合时:provider_id, provider, request_count, total_tokens, total_cost, actual_cost, avg_response_time_ms, success_rate, error_count
|
||||
- 按 API 格式聚合时:api_format, request_count, total_tokens, total_cost, actual_cost, avg_response_time_ms
|
||||
"""
|
||||
if group_by == "model":
|
||||
adapter = AdminUsageByModelAdapter(start_date=start_date, end_date=end_date, limit=limit)
|
||||
@@ -69,6 +78,25 @@ async def get_usage_stats(
|
||||
end_date: Optional[datetime] = None,
|
||||
db: Session = Depends(get_db),
|
||||
):
|
||||
"""
|
||||
获取使用情况总体统计
|
||||
|
||||
获取指定时间范围内的使用情况总体统计数据。
|
||||
|
||||
**查询参数**:
|
||||
- `start_date`: 可选,开始日期(ISO 格式)
|
||||
- `end_date`: 可选,结束日期(ISO 格式)
|
||||
|
||||
**返回字段**:
|
||||
- `total_requests`: 总请求数
|
||||
- `total_tokens`: 总 token 数
|
||||
- `total_cost`: 总成本(美元)
|
||||
- `total_actual_cost`: 实际总成本(美元)
|
||||
- `avg_response_time`: 平均响应时间(秒)
|
||||
- `error_count`: 错误请求数
|
||||
- `error_rate`: 错误率(百分比)
|
||||
- `cache_stats`: 缓存统计信息(cache_creation_tokens, cache_read_tokens, cache_creation_cost, cache_read_cost)
|
||||
"""
|
||||
adapter = AdminUsageStatsAdapter(start_date=start_date, end_date=end_date)
|
||||
return await pipeline.run(adapter=adapter, http_request=request, db=db, mode=adapter.mode)
|
||||
|
||||
@@ -79,9 +107,12 @@ async def get_activity_heatmap(
|
||||
db: Session = Depends(get_db),
|
||||
):
|
||||
"""
|
||||
Get activity heatmap data for the past 365 days.
|
||||
获取活动热力图数据
|
||||
|
||||
This endpoint is cached for 5 minutes to reduce database load.
|
||||
获取过去 365 天的活动热力图数据。此接口缓存 5 分钟以减少数据库负载。
|
||||
|
||||
**返回字段**:
|
||||
- 按日期聚合的请求数、token 数、成本等统计数据
|
||||
"""
|
||||
adapter = AdminActivityHeatmapAdapter()
|
||||
return await pipeline.run(adapter=adapter, http_request=request, db=db, mode=adapter.mode)
|
||||
@@ -102,6 +133,33 @@ async def get_usage_records(
|
||||
offset: int = Query(0, ge=0),
|
||||
db: Session = Depends(get_db),
|
||||
):
|
||||
"""
|
||||
获取使用记录列表
|
||||
|
||||
获取详细的使用记录列表,支持多种筛选条件。
|
||||
|
||||
**查询参数**:
|
||||
- `start_date`: 可选,开始日期(ISO 格式)
|
||||
- `end_date`: 可选,结束日期(ISO 格式)
|
||||
- `search`: 可选,通用搜索关键词(支持用户名、密钥名、模型名、提供商名模糊搜索,多个关键词用空格分隔)
|
||||
- `user_id`: 可选,用户 ID 筛选
|
||||
- `username`: 可选,用户名模糊搜索
|
||||
- `model`: 可选,模型名模糊搜索
|
||||
- `provider`: 可选,提供商名称搜索
|
||||
- `status`: 可选,状态筛选(stream: 流式请求,standard: 标准请求,error: 错误请求,pending: 等待中,streaming: 流式中,completed: 已完成,failed: 失败,active: 活跃请求)
|
||||
- `limit`: 返回数量限制,默认 100,最大 500
|
||||
- `offset`: 分页偏移量,默认 0
|
||||
|
||||
**返回字段**:
|
||||
- `records`: 使用记录列表,包含 id, user_id, user_email, username, api_key, provider, model, target_model,
|
||||
input_tokens, output_tokens, cache_creation_input_tokens, cache_read_input_tokens, total_tokens,
|
||||
cost, actual_cost, rate_multiplier, response_time_ms, first_byte_time_ms, created_at, is_stream,
|
||||
input_price_per_1m, output_price_per_1m, cache_creation_price_per_1m, cache_read_price_per_1m,
|
||||
status_code, error_message, status, has_fallback, api_format, api_key_name, request_metadata
|
||||
- `total`: 符合条件的总记录数
|
||||
- `limit`: 当前分页限制
|
||||
- `offset`: 当前分页偏移量
|
||||
"""
|
||||
adapter = AdminUsageRecordsAdapter(
|
||||
start_date=start_date,
|
||||
end_date=end_date,
|
||||
@@ -124,10 +182,19 @@ async def get_active_requests(
|
||||
db: Session = Depends(get_db),
|
||||
):
|
||||
"""
|
||||
获取活跃请求的状态(轻量级接口,用于前端轮询)
|
||||
获取活跃请求的状态
|
||||
|
||||
获取当前活跃(pending/streaming 状态)请求的状态信息。这是一个轻量级接口,适合前端轮询。
|
||||
|
||||
**查询参数**:
|
||||
- `ids`: 可选,逗号分隔的请求 ID 列表,用于查询特定请求的状态
|
||||
|
||||
**行为说明**:
|
||||
- 如果提供 ids 参数,只返回这些 ID 对应请求的最新状态
|
||||
- 如果不提供 ids,返回所有 pending/streaming 状态的请求
|
||||
|
||||
**返回字段**:
|
||||
- `requests`: 活跃请求列表,包含请求状态信息
|
||||
"""
|
||||
adapter = AdminActiveRequestsAdapter(ids=ids)
|
||||
return await pipeline.run(adapter=adapter, http_request=request, db=db, mode=adapter.mode)
|
||||
@@ -142,9 +209,48 @@ async def get_usage_detail(
|
||||
db: Session = Depends(get_db),
|
||||
):
|
||||
"""
|
||||
Get detailed information of a specific usage record.
|
||||
获取使用记录详情
|
||||
|
||||
Includes request/response headers and body.
|
||||
获取指定使用记录的详细信息,包括请求/响应的头部和正文。
|
||||
|
||||
**路径参数**:
|
||||
- `usage_id`: 使用记录 ID
|
||||
|
||||
**返回字段**:
|
||||
- `id`: 记录 ID
|
||||
- `request_id`: 请求 ID
|
||||
- `user`: 用户信息(id, username, email)
|
||||
- `api_key`: API Key 信息(id, name, display)
|
||||
- `provider`: 提供商名称
|
||||
- `api_format`: API 格式
|
||||
- `model`: 请求的模型名称
|
||||
- `target_model`: 映射后的目标模型名称
|
||||
- `tokens`: Token 统计(input, output, total)
|
||||
- `cost`: 成本统计(input, output, total)
|
||||
- `cache_creation_input_tokens`: 缓存创建输入 token 数
|
||||
- `cache_read_input_tokens`: 缓存读取输入 token 数
|
||||
- `cache_creation_cost`: 缓存创建成本
|
||||
- `cache_read_cost`: 缓存读取成本
|
||||
- `request_cost`: 请求成本
|
||||
- `input_price_per_1m`: 输入价格(每百万 token)
|
||||
- `output_price_per_1m`: 输出价格(每百万 token)
|
||||
- `cache_creation_price_per_1m`: 缓存创建价格(每百万 token)
|
||||
- `cache_read_price_per_1m`: 缓存读取价格(每百万 token)
|
||||
- `price_per_request`: 每请求价格
|
||||
- `request_type`: 请求类型
|
||||
- `is_stream`: 是否为流式请求
|
||||
- `status_code`: HTTP 状态码
|
||||
- `error_message`: 错误信息
|
||||
- `response_time_ms`: 响应时间(毫秒)
|
||||
- `first_byte_time_ms`: 首字节时间(TTFB,毫秒)
|
||||
- `created_at`: 创建时间
|
||||
- `request_headers`: 请求头
|
||||
- `request_body`: 请求体
|
||||
- `provider_request_headers`: 提供商请求头
|
||||
- `response_headers`: 响应头
|
||||
- `response_body`: 响应体
|
||||
- `metadata`: 提供商响应元数据
|
||||
- `tiered_pricing`: 阶梯计费信息(如适用)
|
||||
"""
|
||||
adapter = AdminUsageDetailAdapter(usage_id=usage_id)
|
||||
return await pipeline.run(adapter=adapter, http_request=request, db=db, mode=adapter.mode)
|
||||
|
||||
@@ -26,6 +26,18 @@ pipeline = ApiRequestPipeline()
|
||||
# 管理员端点
|
||||
@router.post("")
|
||||
async def create_user_endpoint(request: Request, db: Session = Depends(get_db)):
|
||||
"""
|
||||
创建用户
|
||||
|
||||
创建新用户账号(管理员专用)。
|
||||
|
||||
**请求体**:
|
||||
- `email`: 邮箱地址
|
||||
- `username`: 用户名
|
||||
- `password`: 密码
|
||||
- `role`: 角色(user/admin)
|
||||
- `quota_usd`: 配额(USD)
|
||||
"""
|
||||
adapter = AdminCreateUserAdapter()
|
||||
return await pipeline.run(adapter=adapter, http_request=request, db=db, mode=adapter.mode)
|
||||
|
||||
@@ -33,18 +45,33 @@ async def create_user_endpoint(request: Request, db: Session = Depends(get_db)):
|
||||
@router.get("")
|
||||
async def list_users(
|
||||
request: Request,
|
||||
skip: int = Query(0, ge=0),
|
||||
limit: int = Query(100, ge=1, le=1000),
|
||||
role: Optional[str] = None,
|
||||
is_active: Optional[bool] = None,
|
||||
skip: int = Query(0, ge=0, description="跳过记录数"),
|
||||
limit: int = Query(100, ge=1, le=1000, description="返回记录数"),
|
||||
role: Optional[str] = Query(None, description="按角色筛选(user/admin)"),
|
||||
is_active: Optional[bool] = Query(None, description="按状态筛选"),
|
||||
db: Session = Depends(get_db),
|
||||
):
|
||||
"""
|
||||
获取用户列表
|
||||
|
||||
分页获取用户列表,支持按角色和状态筛选。
|
||||
|
||||
**返回字段**: id, email, username, role, quota_usd, used_usd, is_active, created_at 等
|
||||
"""
|
||||
adapter = AdminListUsersAdapter(skip=skip, limit=limit, role=role, is_active=is_active)
|
||||
return await pipeline.run(adapter=adapter, http_request=request, db=db, mode=adapter.mode)
|
||||
|
||||
|
||||
@router.get("/{user_id}")
|
||||
async def get_user(user_id: str, request: Request, db: Session = Depends(get_db)): # UUID
|
||||
async def get_user(user_id: str, request: Request, db: Session = Depends(get_db)):
|
||||
"""
|
||||
获取用户详情
|
||||
|
||||
获取指定用户的详细信息。
|
||||
|
||||
**路径参数**:
|
||||
- `user_id`: 用户 ID (UUID)
|
||||
"""
|
||||
adapter = AdminGetUserAdapter(user_id=user_id)
|
||||
return await pipeline.run(adapter=adapter, http_request=request, db=db, mode=adapter.mode)
|
||||
|
||||
@@ -55,19 +82,51 @@ async def update_user(
|
||||
request: Request,
|
||||
db: Session = Depends(get_db),
|
||||
):
|
||||
"""
|
||||
更新用户信息
|
||||
|
||||
更新指定用户的信息,包括角色、配额、权限等。
|
||||
|
||||
**路径参数**:
|
||||
- `user_id`: 用户 ID (UUID)
|
||||
|
||||
**请求体** (均为可选):
|
||||
- `email`: 邮箱地址
|
||||
- `username`: 用户名
|
||||
- `role`: 角色
|
||||
- `quota_usd`: 配额
|
||||
- `is_active`: 是否启用
|
||||
- `allowed_providers`: 允许的提供商列表
|
||||
- `allowed_models`: 允许的模型列表
|
||||
"""
|
||||
adapter = AdminUpdateUserAdapter(user_id=user_id)
|
||||
return await pipeline.run(adapter=adapter, http_request=request, db=db, mode=adapter.mode)
|
||||
|
||||
|
||||
@router.delete("/{user_id}")
|
||||
async def delete_user(user_id: str, request: Request, db: Session = Depends(get_db)): # UUID
|
||||
async def delete_user(user_id: str, request: Request, db: Session = Depends(get_db)):
|
||||
"""
|
||||
删除用户
|
||||
|
||||
永久删除指定用户。不能删除最后一个管理员账户。
|
||||
|
||||
**路径参数**:
|
||||
- `user_id`: 用户 ID (UUID)
|
||||
"""
|
||||
adapter = AdminDeleteUserAdapter(user_id=user_id)
|
||||
return await pipeline.run(adapter=adapter, http_request=request, db=db, mode=adapter.mode)
|
||||
|
||||
|
||||
@router.patch("/{user_id}/quota")
|
||||
async def reset_user_quota(user_id: str, request: Request, db: Session = Depends(get_db)):
|
||||
"""Reset user quota (set used_usd to 0)"""
|
||||
"""
|
||||
重置用户配额
|
||||
|
||||
将用户的已用配额(used_usd)重置为 0。
|
||||
|
||||
**路径参数**:
|
||||
- `user_id`: 用户 ID (UUID)
|
||||
"""
|
||||
adapter = AdminResetUserQuotaAdapter(user_id=user_id)
|
||||
return await pipeline.run(adapter=adapter, http_request=request, db=db, mode=adapter.mode)
|
||||
|
||||
@@ -76,10 +135,17 @@ async def reset_user_quota(user_id: str, request: Request, db: Session = Depends
|
||||
async def get_user_api_keys(
|
||||
user_id: str,
|
||||
request: Request,
|
||||
is_active: Optional[bool] = None,
|
||||
is_active: Optional[bool] = Query(None, description="按状态筛选"),
|
||||
db: Session = Depends(get_db),
|
||||
):
|
||||
"""获取用户的所有API Keys(不包括独立Keys)"""
|
||||
"""
|
||||
获取用户的 API 密钥列表
|
||||
|
||||
获取指定用户的所有 API 密钥(不包括独立密钥)。
|
||||
|
||||
**路径参数**:
|
||||
- `user_id`: 用户 ID (UUID)
|
||||
"""
|
||||
adapter = AdminGetUserKeysAdapter(user_id=user_id, is_active=is_active)
|
||||
return await pipeline.run(adapter=adapter, http_request=request, db=db, mode=adapter.mode)
|
||||
|
||||
@@ -90,7 +156,23 @@ async def create_user_api_key(
|
||||
request: Request,
|
||||
db: Session = Depends(get_db),
|
||||
):
|
||||
"""为用户创建API Key"""
|
||||
"""
|
||||
为用户创建 API 密钥
|
||||
|
||||
为指定用户创建新的 API 密钥。
|
||||
|
||||
**路径参数**:
|
||||
- `user_id`: 用户 ID (UUID)
|
||||
|
||||
**请求体**:
|
||||
- `name`: 密钥名称
|
||||
- `allowed_providers`: 允许的提供商(可选)
|
||||
- `allowed_models`: 允许的模型(可选)
|
||||
- `rate_limit`: 速率限制(可选)
|
||||
- `expire_days`: 过期天数(可选)
|
||||
|
||||
**返回**: 包含完整密钥值的响应(仅此一次显示)
|
||||
"""
|
||||
adapter = AdminCreateUserKeyAdapter(user_id=user_id)
|
||||
return await pipeline.run(adapter=adapter, http_request=request, db=db, mode=adapter.mode)
|
||||
|
||||
@@ -102,7 +184,15 @@ async def delete_user_api_key(
|
||||
request: Request,
|
||||
db: Session = Depends(get_db),
|
||||
):
|
||||
"""删除用户的API Key"""
|
||||
"""
|
||||
删除用户的 API 密钥
|
||||
|
||||
删除指定用户的指定 API 密钥。
|
||||
|
||||
**路径参数**:
|
||||
- `user_id`: 用户 ID (UUID)
|
||||
- `key_id`: 密钥 ID
|
||||
"""
|
||||
adapter = AdminDeleteUserKeyAdapter(user_id=user_id, key_id=key_id)
|
||||
return await pipeline.run(adapter=adapter, http_request=request, db=db, mode=adapter.mode)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user