mirror of
https://github.com/fawney19/Aether.git
synced 2026-01-03 08:12:26 +08:00
717 lines
23 KiB
Python
717 lines
23 KiB
Python
|
|
"""
|
|||
|
|
API端点请求/响应模型定义
|
|||
|
|
"""
|
|||
|
|
|
|||
|
|
import re
|
|||
|
|
from datetime import datetime
|
|||
|
|
from typing import Any, Dict, List, Optional
|
|||
|
|
|
|||
|
|
from pydantic import BaseModel, Field, field_validator
|
|||
|
|
|
|||
|
|
from ..core.enums import UserRole
|
|||
|
|
|
|||
|
|
|
|||
|
|
# ========== 认证相关 ==========
|
|||
|
|
class LoginRequest(BaseModel):
|
|||
|
|
"""登录请求"""
|
|||
|
|
|
|||
|
|
email: str = Field(..., min_length=3, max_length=255, description="邮箱地址")
|
|||
|
|
password: str = Field(..., min_length=1, max_length=128, description="密码")
|
|||
|
|
|
|||
|
|
@classmethod
|
|||
|
|
@field_validator("email")
|
|||
|
|
def validate_email(cls, v):
|
|||
|
|
"""验证邮箱格式"""
|
|||
|
|
email_pattern = r"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$"
|
|||
|
|
if not re.match(email_pattern, v):
|
|||
|
|
raise ValueError("邮箱格式无效")
|
|||
|
|
return v.lower()
|
|||
|
|
|
|||
|
|
@classmethod
|
|||
|
|
@field_validator("password")
|
|||
|
|
def validate_password(cls, v):
|
|||
|
|
"""验证密码不为空且去除前后空格"""
|
|||
|
|
v = v.strip()
|
|||
|
|
if not v:
|
|||
|
|
raise ValueError("密码不能为空")
|
|||
|
|
return v
|
|||
|
|
|
|||
|
|
|
|||
|
|
class LoginResponse(BaseModel):
|
|||
|
|
"""登录响应"""
|
|||
|
|
|
|||
|
|
access_token: str
|
|||
|
|
refresh_token: str # 刷新令牌
|
|||
|
|
token_type: str = "bearer"
|
|||
|
|
expires_in: int = 86400 # Token有效期(秒),默认24小时
|
|||
|
|
user_id: str
|
|||
|
|
email: str
|
|||
|
|
username: str
|
|||
|
|
role: str
|
|||
|
|
|
|||
|
|
|
|||
|
|
class RefreshTokenRequest(BaseModel):
|
|||
|
|
"""刷新令牌请求"""
|
|||
|
|
|
|||
|
|
refresh_token: str = Field(..., description="刷新令牌")
|
|||
|
|
|
|||
|
|
|
|||
|
|
class RefreshTokenResponse(BaseModel):
|
|||
|
|
"""刷新令牌响应"""
|
|||
|
|
|
|||
|
|
access_token: str
|
|||
|
|
refresh_token: str # 返回新的刷新令牌
|
|||
|
|
token_type: str = "bearer"
|
|||
|
|
expires_in: int = 86400 # Token有效期(秒),默认24小时
|
|||
|
|
|
|||
|
|
|
|||
|
|
class RegisterRequest(BaseModel):
|
|||
|
|
"""注册请求"""
|
|||
|
|
|
|||
|
|
email: str = Field(..., min_length=3, max_length=255, description="邮箱地址")
|
|||
|
|
username: str = Field(..., min_length=2, max_length=50, description="用户名")
|
|||
|
|
password: str = Field(..., min_length=6, max_length=128, description="密码")
|
|||
|
|
|
|||
|
|
@classmethod
|
|||
|
|
@field_validator("email")
|
|||
|
|
def validate_email(cls, v):
|
|||
|
|
"""验证邮箱格式"""
|
|||
|
|
email_pattern = r"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$"
|
|||
|
|
if not re.match(email_pattern, v):
|
|||
|
|
raise ValueError("邮箱格式无效")
|
|||
|
|
return v.lower()
|
|||
|
|
|
|||
|
|
@classmethod
|
|||
|
|
@field_validator("username")
|
|||
|
|
def validate_username(cls, v):
|
|||
|
|
"""验证用户名格式"""
|
|||
|
|
v = v.strip()
|
|||
|
|
if not v:
|
|||
|
|
raise ValueError("用户名不能为空")
|
|||
|
|
if not re.match(r"^[a-zA-Z0-9_-]+$", v):
|
|||
|
|
raise ValueError("用户名只能包含字母、数字、下划线和短横线")
|
|||
|
|
return v
|
|||
|
|
|
|||
|
|
@classmethod
|
|||
|
|
@field_validator("password")
|
|||
|
|
def validate_password(cls, v):
|
|||
|
|
"""验证密码强度"""
|
|||
|
|
if len(v) < 6:
|
|||
|
|
raise ValueError("密码至少需要6个字符")
|
|||
|
|
if not re.search(r"[A-Z]", v):
|
|||
|
|
raise ValueError("密码必须包含至少一个大写字母")
|
|||
|
|
if not re.search(r"[a-z]", v):
|
|||
|
|
raise ValueError("密码必须包含至少一个小写字母")
|
|||
|
|
if not re.search(r"\d", v):
|
|||
|
|
raise ValueError("密码必须包含至少一个数字")
|
|||
|
|
return v
|
|||
|
|
|
|||
|
|
|
|||
|
|
class RegisterResponse(BaseModel):
|
|||
|
|
"""注册响应"""
|
|||
|
|
|
|||
|
|
user_id: str
|
|||
|
|
email: str
|
|||
|
|
username: str
|
|||
|
|
message: str
|
|||
|
|
|
|||
|
|
|
|||
|
|
class LogoutResponse(BaseModel):
|
|||
|
|
"""登出响应"""
|
|||
|
|
|
|||
|
|
message: str
|
|||
|
|
success: bool
|
|||
|
|
|
|||
|
|
|
|||
|
|
# ========== 用户管理 ==========
|
|||
|
|
class CreateUserRequest(BaseModel):
|
|||
|
|
"""创建用户请求"""
|
|||
|
|
|
|||
|
|
username: str = Field(..., min_length=2, max_length=50, description="用户名")
|
|||
|
|
password: str = Field(..., min_length=6, max_length=128, description="密码")
|
|||
|
|
email: str = Field(..., min_length=3, max_length=255, description="邮箱地址")
|
|||
|
|
role: Optional[UserRole] = Field(UserRole.USER, description="用户角色")
|
|||
|
|
quota_usd: Optional[float] = Field(default=10.0, description="USD配额,null表示无限制")
|
|||
|
|
|
|||
|
|
@field_validator("quota_usd", mode="before")
|
|||
|
|
@classmethod
|
|||
|
|
def validate_quota_usd(cls, v):
|
|||
|
|
"""验证配额值,允许null表示无限制"""
|
|||
|
|
if v is None:
|
|||
|
|
return None
|
|||
|
|
if isinstance(v, (int, float)) and v >= 0 and v <= 10000:
|
|||
|
|
return float(v)
|
|||
|
|
if isinstance(v, (int, float)):
|
|||
|
|
raise ValueError("配额必须在 0-10000 范围内")
|
|||
|
|
return v
|
|||
|
|
|
|||
|
|
@classmethod
|
|||
|
|
@field_validator("email")
|
|||
|
|
def validate_email(cls, v):
|
|||
|
|
"""验证邮箱格式"""
|
|||
|
|
v = v.strip()
|
|||
|
|
if not v:
|
|||
|
|
raise ValueError("邮箱不能为空")
|
|||
|
|
email_pattern = r"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$"
|
|||
|
|
if not re.match(email_pattern, v):
|
|||
|
|
raise ValueError("邮箱格式无效")
|
|||
|
|
return v.lower()
|
|||
|
|
|
|||
|
|
@classmethod
|
|||
|
|
@field_validator("username")
|
|||
|
|
def validate_username(cls, v):
|
|||
|
|
"""验证用户名格式"""
|
|||
|
|
v = v.strip()
|
|||
|
|
if not v:
|
|||
|
|
raise ValueError("用户名不能为空")
|
|||
|
|
if not re.match(r"^[a-zA-Z0-9_-]+$", v):
|
|||
|
|
raise ValueError("用户名只能包含字母、数字、下划线和短横线")
|
|||
|
|
return v
|
|||
|
|
|
|||
|
|
@classmethod
|
|||
|
|
@field_validator("password")
|
|||
|
|
def validate_password(cls, v):
|
|||
|
|
"""验证密码强度"""
|
|||
|
|
if len(v) < 6:
|
|||
|
|
raise ValueError("密码至少需要6个字符")
|
|||
|
|
if not re.search(r"[A-Z]", v):
|
|||
|
|
raise ValueError("密码必须包含至少一个大写字母")
|
|||
|
|
if not re.search(r"[a-z]", v):
|
|||
|
|
raise ValueError("密码必须包含至少一个小写字母")
|
|||
|
|
if not re.search(r"\d", v):
|
|||
|
|
raise ValueError("密码必须包含至少一个数字")
|
|||
|
|
return v
|
|||
|
|
|
|||
|
|
|
|||
|
|
class UpdateUserRequest(BaseModel):
|
|||
|
|
"""更新用户请求"""
|
|||
|
|
|
|||
|
|
email: Optional[str] = None
|
|||
|
|
username: Optional[str] = None
|
|||
|
|
password: Optional[str] = None
|
|||
|
|
role: Optional[UserRole] = None
|
|||
|
|
allowed_providers: Optional[List[str]] = None # 允许使用的提供商 ID 列表
|
|||
|
|
allowed_endpoints: Optional[List[str]] = None # 允许使用的端点 ID 列表
|
|||
|
|
allowed_models: Optional[List[str]] = None # 允许使用的模型名称列表
|
|||
|
|
quota_usd: Optional[float] = None
|
|||
|
|
is_active: Optional[bool] = None
|
|||
|
|
|
|||
|
|
@field_validator("quota_usd", mode="before")
|
|||
|
|
@classmethod
|
|||
|
|
def validate_quota_usd(cls, v):
|
|||
|
|
"""验证配额值,允许null表示无限制"""
|
|||
|
|
if v is None:
|
|||
|
|
return None
|
|||
|
|
if isinstance(v, (int, float)) and v >= 0 and v <= 10000:
|
|||
|
|
return float(v)
|
|||
|
|
if isinstance(v, (int, float)):
|
|||
|
|
raise ValueError("配额必须在 0-10000 范围内")
|
|||
|
|
return v
|
|||
|
|
|
|||
|
|
|
|||
|
|
class CreateApiKeyRequest(BaseModel):
|
|||
|
|
"""创建API密钥请求"""
|
|||
|
|
|
|||
|
|
name: Optional[str] = None
|
|||
|
|
allowed_providers: Optional[List[str]] = None # 允许使用的提供商 ID 列表
|
|||
|
|
allowed_endpoints: Optional[List[str]] = None # 允许使用的端点 ID 列表
|
|||
|
|
allowed_api_formats: Optional[List[str]] = None # 允许使用的 API 格式列表
|
|||
|
|
allowed_models: Optional[List[str]] = None # 允许使用的模型名称列表
|
|||
|
|
rate_limit: Optional[int] = 100
|
|||
|
|
expire_days: Optional[int] = None # None = 永不过期,数字 = 多少天后过期
|
|||
|
|
initial_balance_usd: Optional[float] = Field(
|
|||
|
|
None, description="初始余额(USD),仅用于独立Key,None = 无限制"
|
|||
|
|
)
|
|||
|
|
is_standalone: bool = Field(False, description="是否为独立余额Key(给非注册用户使用)")
|
|||
|
|
auto_delete_on_expiry: bool = Field(
|
|||
|
|
False, description="过期后是否自动删除(True=物理删除,False=仅禁用)"
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
|
|||
|
|
class UserResponse(BaseModel):
|
|||
|
|
"""用户响应"""
|
|||
|
|
|
|||
|
|
id: str
|
|||
|
|
email: str
|
|||
|
|
username: str
|
|||
|
|
role: UserRole
|
|||
|
|
allowed_providers: Optional[List[str]] = None # 允许使用的提供商 ID 列表
|
|||
|
|
allowed_endpoints: Optional[List[str]] = None # 允许使用的端点 ID 列表
|
|||
|
|
allowed_models: Optional[List[str]] = None # 允许使用的模型名称列表
|
|||
|
|
quota_usd: float
|
|||
|
|
used_usd: float
|
|||
|
|
is_active: bool
|
|||
|
|
created_at: datetime
|
|||
|
|
updated_at: datetime
|
|||
|
|
last_login_at: Optional[datetime]
|
|||
|
|
|
|||
|
|
|
|||
|
|
class ApiKeyResponse(BaseModel):
|
|||
|
|
"""API密钥响应"""
|
|||
|
|
|
|||
|
|
id: str
|
|||
|
|
user_id: str
|
|||
|
|
key: Optional[str] = None # 仅在创建时返回完整密钥
|
|||
|
|
key_display: Optional[str] = None # 脱敏后的密钥显示
|
|||
|
|
name: Optional[str]
|
|||
|
|
total_requests: int
|
|||
|
|
total_tokens: int
|
|||
|
|
total_cost_usd: float
|
|||
|
|
allowed_providers: Optional[List[str]]
|
|||
|
|
allowed_models: Optional[List[str]]
|
|||
|
|
rate_limit: int
|
|||
|
|
is_active: bool
|
|||
|
|
expires_at: Optional[datetime] = None
|
|||
|
|
balance_used_usd: float = 0.0
|
|||
|
|
current_balance_usd: Optional[float] = None # NULL = 无限制
|
|||
|
|
is_standalone: bool = False
|
|||
|
|
force_capabilities: Optional[Dict[str, bool]] = None # 强制开启的能力
|
|||
|
|
created_at: datetime
|
|||
|
|
last_used_at: Optional[datetime]
|
|||
|
|
|
|||
|
|
|
|||
|
|
# ========== 提供商管理 ==========
|
|||
|
|
class ProviderCreate(BaseModel):
|
|||
|
|
"""创建提供商请求
|
|||
|
|
|
|||
|
|
新架构说明:
|
|||
|
|
- Provider 仅包含提供商的元数据和计费配置
|
|||
|
|
- API格式、URL、认证等配置应在 ProviderEndpoint 中设置
|
|||
|
|
- API密钥应在 ProviderAPIKey 中设置
|
|||
|
|
"""
|
|||
|
|
|
|||
|
|
name: str = Field(..., min_length=1, max_length=100, description="提供商唯一标识")
|
|||
|
|
display_name: str = Field(..., min_length=1, max_length=100, description="显示名称")
|
|||
|
|
description: Optional[str] = Field(None, description="提供商描述")
|
|||
|
|
website: Optional[str] = Field(None, max_length=500, description="主站网站")
|
|||
|
|
|
|||
|
|
# Provider 级别的配置
|
|||
|
|
rate_limit: Optional[int] = Field(None, description="每分钟请求限制")
|
|||
|
|
concurrent_limit: Optional[int] = Field(None, description="并发请求限制")
|
|||
|
|
config: Optional[dict] = Field(None, description="额外配置")
|
|||
|
|
is_active: bool = Field(False, description="是否启用(默认false,需要配置API密钥后才能启用)")
|
|||
|
|
|
|||
|
|
|
|||
|
|
class ProviderUpdate(BaseModel):
|
|||
|
|
"""更新提供商请求"""
|
|||
|
|
|
|||
|
|
display_name: Optional[str] = Field(None, min_length=1, max_length=100)
|
|||
|
|
description: Optional[str] = None
|
|||
|
|
website: Optional[str] = Field(None, max_length=500)
|
|||
|
|
api_format: Optional[str] = None
|
|||
|
|
base_url: Optional[str] = None
|
|||
|
|
headers: Optional[dict] = None
|
|||
|
|
timeout: Optional[int] = Field(None, ge=1, le=600)
|
|||
|
|
max_retries: Optional[int] = Field(None, ge=0, le=10)
|
|||
|
|
priority: Optional[int] = None
|
|||
|
|
weight: Optional[float] = Field(None, gt=0)
|
|||
|
|
rate_limit: Optional[int] = None
|
|||
|
|
concurrent_limit: Optional[int] = None
|
|||
|
|
config: Optional[dict] = None
|
|||
|
|
is_active: Optional[bool] = None
|
|||
|
|
|
|||
|
|
|
|||
|
|
class ProviderResponse(BaseModel):
|
|||
|
|
"""提供商响应"""
|
|||
|
|
|
|||
|
|
id: str
|
|||
|
|
name: str
|
|||
|
|
display_name: str
|
|||
|
|
description: Optional[str]
|
|||
|
|
website: Optional[str]
|
|||
|
|
api_format: str
|
|||
|
|
base_url: str
|
|||
|
|
headers: Optional[dict]
|
|||
|
|
timeout: int
|
|||
|
|
max_retries: int
|
|||
|
|
priority: int
|
|||
|
|
weight: float
|
|||
|
|
rate_limit: Optional[int]
|
|||
|
|
concurrent_limit: Optional[int]
|
|||
|
|
config: Optional[dict]
|
|||
|
|
is_active: bool
|
|||
|
|
created_at: datetime
|
|||
|
|
updated_at: datetime
|
|||
|
|
models_count: int = 0
|
|||
|
|
active_models_count: int = 0
|
|||
|
|
model_mappings_count: int = 0
|
|||
|
|
api_keys_count: int = 0
|
|||
|
|
|
|||
|
|
class Config:
|
|||
|
|
from_attributes = True
|
|||
|
|
|
|||
|
|
|
|||
|
|
# ========== 模型管理 ==========
|
|||
|
|
class ModelCreate(BaseModel):
|
|||
|
|
"""创建模型请求 - 价格和能力字段可选,为空时使用 GlobalModel 默认值"""
|
|||
|
|
|
|||
|
|
provider_model_name: str = Field(
|
|||
|
|
..., min_length=1, max_length=200, description="Provider 侧的模型名称"
|
|||
|
|
)
|
|||
|
|
global_model_id: str = Field(..., description="关联的 GlobalModel ID(必填)")
|
|||
|
|
# 按次计费配置 - 可选,为空时使用 GlobalModel 默认值
|
|||
|
|
price_per_request: Optional[float] = Field(
|
|||
|
|
None, ge=0, description="每次请求固定费用,为空使用默认值"
|
|||
|
|
)
|
|||
|
|
# 阶梯计费配置 - 可选,为空时使用 GlobalModel 默认值
|
|||
|
|
tiered_pricing: Optional[dict] = Field(
|
|||
|
|
None, description="阶梯计费配置,为空使用 GlobalModel 默认值"
|
|||
|
|
)
|
|||
|
|
# 能力配置 - 可选,为空时使用 GlobalModel 默认值
|
|||
|
|
supports_vision: Optional[bool] = Field(None, description="是否支持图像输入,为空使用默认值")
|
|||
|
|
supports_function_calling: Optional[bool] = Field(
|
|||
|
|
None, description="是否支持函数调用,为空使用默认值"
|
|||
|
|
)
|
|||
|
|
supports_streaming: Optional[bool] = Field(None, description="是否支持流式输出,为空使用默认值")
|
|||
|
|
supports_extended_thinking: Optional[bool] = Field(
|
|||
|
|
None, description="是否支持扩展思考,为空使用默认值"
|
|||
|
|
)
|
|||
|
|
is_active: bool = Field(True, description="是否启用")
|
|||
|
|
config: Optional[dict] = Field(None, description="额外配置")
|
|||
|
|
|
|||
|
|
|
|||
|
|
class ModelUpdate(BaseModel):
|
|||
|
|
"""更新模型请求"""
|
|||
|
|
|
|||
|
|
provider_model_name: Optional[str] = Field(None, min_length=1, max_length=200)
|
|||
|
|
global_model_id: Optional[str] = None
|
|||
|
|
# 按次计费配置
|
|||
|
|
price_per_request: Optional[float] = Field(None, ge=0, description="每次请求固定费用")
|
|||
|
|
# 阶梯计费配置
|
|||
|
|
tiered_pricing: Optional[dict] = Field(None, description="阶梯计费配置")
|
|||
|
|
supports_vision: Optional[bool] = None
|
|||
|
|
supports_function_calling: Optional[bool] = None
|
|||
|
|
supports_streaming: Optional[bool] = None
|
|||
|
|
supports_extended_thinking: Optional[bool] = None
|
|||
|
|
is_active: Optional[bool] = None
|
|||
|
|
is_available: Optional[bool] = None
|
|||
|
|
config: Optional[dict] = None
|
|||
|
|
|
|||
|
|
|
|||
|
|
class ModelResponse(BaseModel):
|
|||
|
|
"""模型响应 - 包含 Model 配置和关联的 GlobalModel 信息
|
|||
|
|
|
|||
|
|
注意:价格和能力字段返回的是有效值(优先使用 Model 配置,否则使用 GlobalModel 默认值)
|
|||
|
|
"""
|
|||
|
|
|
|||
|
|
id: str
|
|||
|
|
provider_id: str
|
|||
|
|
global_model_id: Optional[str]
|
|||
|
|
provider_model_name: str
|
|||
|
|
|
|||
|
|
# 按次计费配置
|
|||
|
|
price_per_request: Optional[float] = None
|
|||
|
|
# 阶梯计费配置
|
|||
|
|
tiered_pricing: Optional[dict] = None
|
|||
|
|
|
|||
|
|
# Provider 能力配置 - 可选,为空表示使用 GlobalModel 默认值
|
|||
|
|
supports_vision: Optional[bool]
|
|||
|
|
supports_function_calling: Optional[bool]
|
|||
|
|
supports_streaming: Optional[bool]
|
|||
|
|
supports_extended_thinking: Optional[bool]
|
|||
|
|
supports_image_generation: Optional[bool]
|
|||
|
|
|
|||
|
|
# 有效值(合并 Model 配置和 GlobalModel 默认值后的结果)
|
|||
|
|
effective_tiered_pricing: Optional[dict] = None
|
|||
|
|
effective_input_price: Optional[float] = None
|
|||
|
|
effective_output_price: Optional[float] = None
|
|||
|
|
effective_price_per_request: Optional[float] = None
|
|||
|
|
effective_supports_vision: Optional[bool] = None
|
|||
|
|
effective_supports_function_calling: Optional[bool] = None
|
|||
|
|
effective_supports_streaming: Optional[bool] = None
|
|||
|
|
effective_supports_extended_thinking: Optional[bool] = None
|
|||
|
|
effective_supports_image_generation: Optional[bool] = None
|
|||
|
|
|
|||
|
|
# 状态
|
|||
|
|
is_active: bool
|
|||
|
|
is_available: bool
|
|||
|
|
|
|||
|
|
# 时间戳
|
|||
|
|
created_at: datetime
|
|||
|
|
updated_at: datetime
|
|||
|
|
|
|||
|
|
# 关联的 GlobalModel 信息(如果有)
|
|||
|
|
global_model_name: Optional[str] = None
|
|||
|
|
global_model_display_name: Optional[str] = None
|
|||
|
|
|
|||
|
|
class Config:
|
|||
|
|
from_attributes = True
|
|||
|
|
|
|||
|
|
|
|||
|
|
class ModelDetailResponse(BaseModel):
|
|||
|
|
"""模型详细响应 - 包含所有字段(用于需要完整信息的场景)"""
|
|||
|
|
|
|||
|
|
id: str
|
|||
|
|
provider_id: str
|
|||
|
|
name: str
|
|||
|
|
display_name: str
|
|||
|
|
description: Optional[str]
|
|||
|
|
icon_url: Optional[str]
|
|||
|
|
tags: Optional[List[str]]
|
|||
|
|
input_price_per_1m: float
|
|||
|
|
output_price_per_1m: float
|
|||
|
|
cache_creation_price_per_1m: Optional[float]
|
|||
|
|
cache_read_price_per_1m: Optional[float]
|
|||
|
|
supports_vision: bool
|
|||
|
|
supports_function_calling: bool
|
|||
|
|
supports_streaming: bool
|
|||
|
|
is_active: bool
|
|||
|
|
is_available: bool
|
|||
|
|
config: Optional[dict]
|
|||
|
|
created_at: datetime
|
|||
|
|
updated_at: datetime
|
|||
|
|
|
|||
|
|
class Config:
|
|||
|
|
from_attributes = True
|
|||
|
|
|
|||
|
|
|
|||
|
|
# ========== 模型映射 ==========
|
|||
|
|
class ModelMappingCreate(BaseModel):
|
|||
|
|
"""创建模型映射请求(源模型到目标模型的映射)"""
|
|||
|
|
|
|||
|
|
source_model: str = Field(..., min_length=1, max_length=200, description="源模型名或别名")
|
|||
|
|
target_global_model_id: str = Field(..., description="目标 GlobalModel ID")
|
|||
|
|
provider_id: Optional[str] = Field(None, description="Provider ID(为空时表示全局别名)")
|
|||
|
|
mapping_type: str = Field(
|
|||
|
|
"alias",
|
|||
|
|
description="映射类型:alias=按目标模型计费(别名),mapping=按源模型计费(降级映射)",
|
|||
|
|
)
|
|||
|
|
is_active: bool = Field(True, description="是否启用")
|
|||
|
|
|
|||
|
|
|
|||
|
|
class ModelMappingUpdate(BaseModel):
|
|||
|
|
"""更新模型映射请求"""
|
|||
|
|
|
|||
|
|
source_model: Optional[str] = Field(
|
|||
|
|
None, min_length=1, max_length=200, description="源模型名或别名"
|
|||
|
|
)
|
|||
|
|
target_global_model_id: Optional[str] = Field(None, description="目标 GlobalModel ID")
|
|||
|
|
provider_id: Optional[str] = Field(None, description="Provider ID(为空时表示全局别名)")
|
|||
|
|
mapping_type: Optional[str] = Field(
|
|||
|
|
None, description="映射类型:alias=按目标模型计费(别名),mapping=按源模型计费(降级映射)"
|
|||
|
|
)
|
|||
|
|
is_active: Optional[bool] = None
|
|||
|
|
|
|||
|
|
|
|||
|
|
class ModelMappingResponse(BaseModel):
|
|||
|
|
"""模型映射响应"""
|
|||
|
|
|
|||
|
|
id: str
|
|||
|
|
source_model: str
|
|||
|
|
target_global_model_id: str
|
|||
|
|
target_global_model_name: Optional[str]
|
|||
|
|
target_global_model_display_name: Optional[str]
|
|||
|
|
provider_id: Optional[str]
|
|||
|
|
provider_name: Optional[str]
|
|||
|
|
scope: str = Field(..., description="global 或 provider")
|
|||
|
|
mapping_type: str = Field(..., description="映射类型:alias 或 mapping")
|
|||
|
|
is_active: bool
|
|||
|
|
created_at: datetime
|
|||
|
|
updated_at: datetime
|
|||
|
|
|
|||
|
|
class Config:
|
|||
|
|
from_attributes = True
|
|||
|
|
|
|||
|
|
|
|||
|
|
# ========== 系统设置 ==========
|
|||
|
|
class SystemSettingsRequest(BaseModel):
|
|||
|
|
"""系统设置请求"""
|
|||
|
|
|
|||
|
|
default_provider: Optional[str] = None
|
|||
|
|
default_model: Optional[str] = None
|
|||
|
|
enable_usage_tracking: Optional[bool] = None
|
|||
|
|
|
|||
|
|
|
|||
|
|
class SystemSettingsResponse(BaseModel):
|
|||
|
|
"""系统设置响应"""
|
|||
|
|
|
|||
|
|
default_provider: Optional[str]
|
|||
|
|
default_model: Optional[str]
|
|||
|
|
enable_usage_tracking: bool
|
|||
|
|
|
|||
|
|
|
|||
|
|
# ========== 使用统计 ==========
|
|||
|
|
class UsageStatsResponse(BaseModel):
|
|||
|
|
"""使用统计响应"""
|
|||
|
|
|
|||
|
|
total_requests: int
|
|||
|
|
total_tokens: int
|
|||
|
|
total_cost_usd: float
|
|||
|
|
daily_requests: int
|
|||
|
|
daily_tokens: int
|
|||
|
|
daily_cost_usd: float
|
|||
|
|
model_usage: Dict[str, Dict[str, Any]]
|
|||
|
|
provider_usage: Dict[str, Dict[str, Any]]
|
|||
|
|
|
|||
|
|
|
|||
|
|
# ========== 公开API响应模型 ==========
|
|||
|
|
class PublicProviderResponse(BaseModel):
|
|||
|
|
"""公开的提供商信息响应"""
|
|||
|
|
|
|||
|
|
id: str
|
|||
|
|
name: str
|
|||
|
|
display_name: str
|
|||
|
|
description: Optional[str]
|
|||
|
|
website: Optional[str]
|
|||
|
|
is_active: bool
|
|||
|
|
provider_priority: int # 提供商优先级(数字越小越优先)
|
|||
|
|
# 统计信息
|
|||
|
|
models_count: int
|
|||
|
|
active_models_count: int
|
|||
|
|
mappings_count: int
|
|||
|
|
endpoints_count: int # 端点总数
|
|||
|
|
active_endpoints_count: int # 活跃端点数
|
|||
|
|
|
|||
|
|
|
|||
|
|
class PublicModelResponse(BaseModel):
|
|||
|
|
"""公开的模型信息响应"""
|
|||
|
|
|
|||
|
|
id: str
|
|||
|
|
provider_id: str
|
|||
|
|
provider_name: str
|
|||
|
|
provider_display_name: str
|
|||
|
|
name: str
|
|||
|
|
display_name: str
|
|||
|
|
description: Optional[str] = None
|
|||
|
|
tags: Optional[List[str]] = None
|
|||
|
|
icon_url: Optional[str] = None
|
|||
|
|
# 价格信息
|
|||
|
|
input_price_per_1m: Optional[float] = None
|
|||
|
|
output_price_per_1m: Optional[float] = None
|
|||
|
|
cache_creation_price_per_1m: Optional[float] = None
|
|||
|
|
cache_read_price_per_1m: Optional[float] = None
|
|||
|
|
# 功能支持
|
|||
|
|
supports_vision: Optional[bool] = None
|
|||
|
|
supports_function_calling: Optional[bool] = None
|
|||
|
|
supports_streaming: Optional[bool] = None
|
|||
|
|
is_active: bool = True
|
|||
|
|
|
|||
|
|
|
|||
|
|
class PublicModelMappingResponse(BaseModel):
|
|||
|
|
"""公开的模型映射信息响应"""
|
|||
|
|
|
|||
|
|
id: str
|
|||
|
|
source_model: str
|
|||
|
|
target_global_model_id: str
|
|||
|
|
target_global_model_name: Optional[str]
|
|||
|
|
target_global_model_display_name: Optional[str]
|
|||
|
|
provider_id: Optional[str] = None
|
|||
|
|
scope: str = Field(..., description="global 或 provider")
|
|||
|
|
is_active: bool
|
|||
|
|
|
|||
|
|
|
|||
|
|
class ProviderStatsResponse(BaseModel):
|
|||
|
|
"""提供商统计信息响应"""
|
|||
|
|
|
|||
|
|
total_providers: int
|
|||
|
|
active_providers: int
|
|||
|
|
total_models: int
|
|||
|
|
active_models: int
|
|||
|
|
total_mappings: int
|
|||
|
|
supported_formats: List[str]
|
|||
|
|
|
|||
|
|
|
|||
|
|
class PublicGlobalModelResponse(BaseModel):
|
|||
|
|
"""公开的 GlobalModel 信息响应(用户可见)"""
|
|||
|
|
|
|||
|
|
id: str
|
|||
|
|
name: str
|
|||
|
|
display_name: Optional[str] = None
|
|||
|
|
description: Optional[str] = None
|
|||
|
|
icon_url: Optional[str] = None
|
|||
|
|
is_active: bool = True
|
|||
|
|
# 按次计费配置
|
|||
|
|
default_price_per_request: Optional[float] = None
|
|||
|
|
# 阶梯计费配置
|
|||
|
|
default_tiered_pricing: Optional[dict] = None
|
|||
|
|
# 默认能力
|
|||
|
|
default_supports_vision: bool = False
|
|||
|
|
default_supports_function_calling: bool = False
|
|||
|
|
default_supports_streaming: bool = True
|
|||
|
|
default_supports_extended_thinking: bool = False
|
|||
|
|
# Key 能力配置
|
|||
|
|
supported_capabilities: Optional[List[str]] = None
|
|||
|
|
|
|||
|
|
|
|||
|
|
class PublicGlobalModelListResponse(BaseModel):
|
|||
|
|
"""公开的 GlobalModel 列表响应"""
|
|||
|
|
|
|||
|
|
models: List[PublicGlobalModelResponse]
|
|||
|
|
total: int
|
|||
|
|
|
|||
|
|
|
|||
|
|
# ========== 个人中心相关模型 ==========
|
|||
|
|
class UpdateProfileRequest(BaseModel):
|
|||
|
|
"""更新个人信息请求"""
|
|||
|
|
|
|||
|
|
email: Optional[str] = None
|
|||
|
|
username: Optional[str] = None
|
|||
|
|
|
|||
|
|
|
|||
|
|
class UpdatePreferencesRequest(BaseModel):
|
|||
|
|
"""更新偏好设置请求"""
|
|||
|
|
|
|||
|
|
avatar_url: Optional[str] = None
|
|||
|
|
bio: Optional[str] = None
|
|||
|
|
default_provider_id: Optional[int] = None
|
|||
|
|
theme: Optional[str] = None
|
|||
|
|
language: Optional[str] = None
|
|||
|
|
timezone: Optional[str] = None
|
|||
|
|
email_notifications: Optional[bool] = None
|
|||
|
|
usage_alerts: Optional[bool] = None
|
|||
|
|
announcement_notifications: Optional[bool] = None
|
|||
|
|
|
|||
|
|
|
|||
|
|
class ChangePasswordRequest(BaseModel):
|
|||
|
|
"""修改密码请求"""
|
|||
|
|
|
|||
|
|
old_password: str
|
|||
|
|
new_password: str
|
|||
|
|
|
|||
|
|
|
|||
|
|
class CreateMyApiKeyRequest(BaseModel):
|
|||
|
|
"""创建我的API密钥请求"""
|
|||
|
|
|
|||
|
|
name: str
|
|||
|
|
|
|||
|
|
|
|||
|
|
class ProviderConfig(BaseModel):
|
|||
|
|
"""提供商配置"""
|
|||
|
|
|
|||
|
|
provider_id: str = Field(..., description="提供商ID")
|
|||
|
|
priority: int = Field(100, description="优先级(越高越优先)")
|
|||
|
|
weight: float = Field(1.0, description="负载均衡权重")
|
|||
|
|
enabled: bool = Field(True, description="是否启用")
|
|||
|
|
|
|||
|
|
|
|||
|
|
class UpdateApiKeyProvidersRequest(BaseModel):
|
|||
|
|
"""更新API密钥可用提供商请求"""
|
|||
|
|
|
|||
|
|
allowed_providers: Optional[List[ProviderConfig]] = None # 提供商配置列表
|
|||
|
|
|
|||
|
|
|
|||
|
|
# ========== 公告相关模型 ==========
|
|||
|
|
class CreateAnnouncementRequest(BaseModel):
|
|||
|
|
"""创建公告请求"""
|
|||
|
|
|
|||
|
|
title: str
|
|||
|
|
content: str # 支持Markdown
|
|||
|
|
type: str = "info" # info, warning, maintenance, important
|
|||
|
|
priority: int = 0
|
|||
|
|
is_pinned: bool = False
|
|||
|
|
start_time: Optional[datetime] = None
|
|||
|
|
end_time: Optional[datetime] = None
|
|||
|
|
|
|||
|
|
|
|||
|
|
class UpdateAnnouncementRequest(BaseModel):
|
|||
|
|
"""更新公告请求"""
|
|||
|
|
|
|||
|
|
title: Optional[str] = None
|
|||
|
|
content: Optional[str] = None
|
|||
|
|
type: Optional[str] = None
|
|||
|
|
priority: Optional[int] = None
|
|||
|
|
is_active: Optional[bool] = None
|
|||
|
|
is_pinned: Optional[bool] = None
|
|||
|
|
start_time: Optional[datetime] = None
|
|||
|
|
end_time: Optional[datetime] = None
|