mirror of
https://github.com/fawney19/Aether.git
synced 2026-09-02 09:20:22 +08:00
chore: 升级到 Python 3.14 并现代化代码
- 升级 Docker 基础镜像从 Python 3.12 到 3.14 - 更新 pyproject.toml 支持 Python 3.13/3.14 - 移除 Python 3.8/3.9/3.10/3.11 分类器 - 更新 black 和 mypy 配置目标版本 - 将 get_event_loop() 替换为 get_running_loop() 加上 RuntimeError 处理 - 简化 compute_cost_sync 中的 asyncio.run 使用 - Dict/List/Tuple/Set → dict/list/tuple/set (PEP 585) - Optional[T] → T | None (PEP 604) - Union[A, B] → A | B (PEP 604) - 移除废弃的 typing 导入 - 移除不必要的字符串引号注解
This commit is contained in:
@@ -6,9 +6,9 @@
|
||||
|
||||
import re
|
||||
from datetime import datetime
|
||||
from typing import Any, Dict, List, Optional
|
||||
from typing import Any
|
||||
|
||||
from pydantic import BaseModel, Field, field_validator, model_validator
|
||||
from pydantic import BaseModel, Field, field_validator
|
||||
|
||||
from src.core.api_format import APIFormat
|
||||
from src.core.enums import ProviderBillingType
|
||||
@@ -18,8 +18,8 @@ class ProxyConfig(BaseModel):
|
||||
"""代理配置"""
|
||||
|
||||
url: str = Field(..., description="代理 URL (http://, https://, socks5://)")
|
||||
username: Optional[str] = Field(None, max_length=255, description="代理用户名")
|
||||
password: Optional[str] = Field(None, max_length=500, description="代理密码")
|
||||
username: str | None = Field(None, max_length=255, description="代理用户名")
|
||||
password: str | None = Field(None, max_length=500, description="代理密码")
|
||||
enabled: bool = Field(True, description="是否启用代理(false 时保留配置但不使用)")
|
||||
|
||||
@field_validator("url")
|
||||
@@ -54,8 +54,8 @@ class CreateProviderRequest(BaseModel):
|
||||
"""创建 Provider 请求"""
|
||||
|
||||
name: str = Field(..., min_length=1, max_length=100, description="提供商名称(唯一)")
|
||||
description: Optional[str] = Field(None, max_length=1000, description="描述")
|
||||
website: Optional[str] = Field(None, max_length=500, description="官网地址")
|
||||
description: str | None = Field(None, max_length=1000, description="描述")
|
||||
website: str | None = Field(None, max_length=500, description="官网地址")
|
||||
|
||||
@field_validator("name")
|
||||
@classmethod
|
||||
@@ -78,27 +78,27 @@ class CreateProviderRequest(BaseModel):
|
||||
raise ValueError(f"名称包含非法关键字: {keyword}")
|
||||
|
||||
return v
|
||||
billing_type: Optional[str] = Field(
|
||||
billing_type: str | None = Field(
|
||||
ProviderBillingType.PAY_AS_YOU_GO.value, description="计费类型"
|
||||
)
|
||||
monthly_quota_usd: Optional[float] = Field(None, ge=0, description="周期配额(美元)")
|
||||
quota_reset_day: Optional[int] = Field(30, ge=1, le=365, description="配额重置周期(天数)")
|
||||
quota_last_reset_at: Optional[datetime] = Field(None, description="当前周期开始时间")
|
||||
quota_expires_at: Optional[datetime] = Field(None, description="配额过期时间")
|
||||
provider_priority: Optional[int] = Field(100, ge=0, le=1000, description="提供商优先级(数字越小越优先)")
|
||||
is_active: Optional[bool] = Field(True, description="是否启用")
|
||||
concurrent_limit: Optional[int] = Field(None, ge=0, description="并发限制")
|
||||
monthly_quota_usd: float | None = Field(None, ge=0, description="周期配额(美元)")
|
||||
quota_reset_day: int | None = Field(30, ge=1, le=365, description="配额重置周期(天数)")
|
||||
quota_last_reset_at: datetime | None = Field(None, description="当前周期开始时间")
|
||||
quota_expires_at: datetime | None = Field(None, description="配额过期时间")
|
||||
provider_priority: int | None = Field(100, ge=0, le=1000, description="提供商优先级(数字越小越优先)")
|
||||
is_active: bool | None = Field(True, description="是否启用")
|
||||
concurrent_limit: int | None = Field(None, ge=0, description="并发限制")
|
||||
# 请求配置(从 Endpoint 迁移)
|
||||
max_retries: Optional[int] = Field(2, ge=0, le=10, description="最大重试次数")
|
||||
proxy: Optional[ProxyConfig] = Field(None, description="代理配置")
|
||||
max_retries: int | None = Field(2, ge=0, le=10, description="最大重试次数")
|
||||
proxy: ProxyConfig | None = Field(None, description="代理配置")
|
||||
# 超时配置(秒),为空时使用全局配置
|
||||
stream_first_byte_timeout: Optional[float] = Field(None, ge=1, le=300, description="流式请求首字节超时(秒)")
|
||||
request_timeout: Optional[float] = Field(None, ge=1, le=600, description="非流式请求整体超时(秒)")
|
||||
config: Optional[Dict[str, Any]] = Field(None, description="其他配置")
|
||||
stream_first_byte_timeout: float | None = Field(None, ge=1, le=300, description="流式请求首字节超时(秒)")
|
||||
request_timeout: float | None = Field(None, ge=1, le=600, description="非流式请求整体超时(秒)")
|
||||
config: dict[str, Any] | None = Field(None, description="其他配置")
|
||||
|
||||
@field_validator("name", "description")
|
||||
@classmethod
|
||||
def sanitize_text(cls, v: Optional[str]) -> Optional[str]:
|
||||
def sanitize_text(cls, v: str | None) -> str | None:
|
||||
"""清理文本输入,防止 XSS"""
|
||||
if v is None:
|
||||
return v
|
||||
@@ -119,7 +119,7 @@ class CreateProviderRequest(BaseModel):
|
||||
|
||||
@field_validator("website")
|
||||
@classmethod
|
||||
def validate_website(cls, v: Optional[str]) -> Optional[str]:
|
||||
def validate_website(cls, v: str | None) -> str | None:
|
||||
"""验证网站地址"""
|
||||
if v is None or v.strip() == "":
|
||||
return None
|
||||
@@ -134,7 +134,7 @@ class CreateProviderRequest(BaseModel):
|
||||
|
||||
@field_validator("billing_type")
|
||||
@classmethod
|
||||
def validate_billing_type(cls, v: Optional[str]) -> Optional[str]:
|
||||
def validate_billing_type(cls, v: str | None) -> str | None:
|
||||
"""验证计费类型"""
|
||||
if v is None:
|
||||
return ProviderBillingType.PAY_AS_YOU_GO.value
|
||||
@@ -150,24 +150,24 @@ class CreateProviderRequest(BaseModel):
|
||||
class UpdateProviderRequest(BaseModel):
|
||||
"""更新 Provider 请求"""
|
||||
|
||||
name: Optional[str] = Field(None, min_length=1, max_length=100)
|
||||
description: Optional[str] = Field(None, max_length=1000)
|
||||
website: Optional[str] = Field(None, max_length=500)
|
||||
billing_type: Optional[str] = None
|
||||
monthly_quota_usd: Optional[float] = Field(None, ge=0)
|
||||
quota_reset_day: Optional[int] = Field(None, ge=1, le=365)
|
||||
quota_last_reset_at: Optional[datetime] = None
|
||||
quota_expires_at: Optional[datetime] = None
|
||||
provider_priority: Optional[int] = Field(None, ge=0, le=1000)
|
||||
is_active: Optional[bool] = None
|
||||
concurrent_limit: Optional[int] = Field(None, ge=0)
|
||||
name: str | None = Field(None, min_length=1, max_length=100)
|
||||
description: str | None = Field(None, max_length=1000)
|
||||
website: str | None = Field(None, max_length=500)
|
||||
billing_type: str | None = None
|
||||
monthly_quota_usd: float | None = Field(None, ge=0)
|
||||
quota_reset_day: int | None = Field(None, ge=1, le=365)
|
||||
quota_last_reset_at: datetime | None = None
|
||||
quota_expires_at: datetime | None = None
|
||||
provider_priority: int | None = Field(None, ge=0, le=1000)
|
||||
is_active: bool | None = None
|
||||
concurrent_limit: int | None = Field(None, ge=0)
|
||||
# 请求配置(从 Endpoint 迁移)
|
||||
max_retries: Optional[int] = Field(None, ge=0, le=10, description="最大重试次数")
|
||||
proxy: Optional[ProxyConfig] = Field(None, description="代理配置")
|
||||
max_retries: int | None = Field(None, ge=0, le=10, description="最大重试次数")
|
||||
proxy: ProxyConfig | None = Field(None, description="代理配置")
|
||||
# 超时配置(秒),为空时使用全局配置
|
||||
stream_first_byte_timeout: Optional[float] = Field(None, ge=1, le=300, description="流式请求首字节超时(秒)")
|
||||
request_timeout: Optional[float] = Field(None, ge=1, le=600, description="非流式请求整体超时(秒)")
|
||||
config: Optional[Dict[str, Any]] = None
|
||||
stream_first_byte_timeout: float | None = Field(None, ge=1, le=300, description="流式请求首字节超时(秒)")
|
||||
request_timeout: float | None = Field(None, ge=1, le=600, description="非流式请求整体超时(秒)")
|
||||
config: dict[str, Any] | None = None
|
||||
|
||||
# 复用相同的验证器
|
||||
_sanitize_text = field_validator("name", "description")(
|
||||
@@ -186,12 +186,12 @@ class CreateEndpointRequest(BaseModel):
|
||||
name: str = Field(..., min_length=1, max_length=100, description="Endpoint 名称")
|
||||
base_url: str = Field(..., min_length=1, max_length=500, description="API 基础 URL")
|
||||
api_format: str = Field(..., description="API 格式(CLAUDE 或 OPENAI)")
|
||||
custom_path: Optional[str] = Field(None, max_length=200, description="自定义路径")
|
||||
priority: Optional[int] = Field(100, ge=0, le=1000, description="优先级")
|
||||
is_active: Optional[bool] = Field(True, description="是否启用")
|
||||
concurrent_limit: Optional[int] = Field(None, ge=0, description="并发限制")
|
||||
config: Optional[Dict[str, Any]] = Field(None, description="其他配置")
|
||||
proxy: Optional[ProxyConfig] = Field(None, description="代理配置")
|
||||
custom_path: str | None = Field(None, max_length=200, description="自定义路径")
|
||||
priority: int | None = Field(100, ge=0, le=1000, description="优先级")
|
||||
is_active: bool | None = Field(True, description="是否启用")
|
||||
concurrent_limit: int | None = Field(None, ge=0, description="并发限制")
|
||||
config: dict[str, Any] | None = Field(None, description="其他配置")
|
||||
proxy: ProxyConfig | None = Field(None, description="代理配置")
|
||||
|
||||
@field_validator("name")
|
||||
@classmethod
|
||||
@@ -223,7 +223,7 @@ class CreateEndpointRequest(BaseModel):
|
||||
|
||||
@field_validator("custom_path")
|
||||
@classmethod
|
||||
def validate_custom_path(cls, v: Optional[str]) -> Optional[str]:
|
||||
def validate_custom_path(cls, v: str | None) -> str | None:
|
||||
"""验证自定义路径"""
|
||||
if v is None:
|
||||
return v
|
||||
@@ -238,15 +238,15 @@ class CreateEndpointRequest(BaseModel):
|
||||
class UpdateEndpointRequest(BaseModel):
|
||||
"""更新 Endpoint 请求"""
|
||||
|
||||
name: Optional[str] = Field(None, min_length=1, max_length=100)
|
||||
base_url: Optional[str] = Field(None, min_length=1, max_length=500)
|
||||
api_format: Optional[str] = None
|
||||
custom_path: Optional[str] = Field(None, max_length=200)
|
||||
priority: Optional[int] = Field(None, ge=0, le=1000)
|
||||
is_active: Optional[bool] = None
|
||||
concurrent_limit: Optional[int] = Field(None, ge=0)
|
||||
config: Optional[Dict[str, Any]] = None
|
||||
proxy: Optional[ProxyConfig] = Field(None, description="代理配置")
|
||||
name: str | None = Field(None, min_length=1, max_length=100)
|
||||
base_url: str | None = Field(None, min_length=1, max_length=500)
|
||||
api_format: str | None = None
|
||||
custom_path: str | None = Field(None, max_length=200)
|
||||
priority: int | None = Field(None, ge=0, le=1000)
|
||||
is_active: bool | None = None
|
||||
concurrent_limit: int | None = Field(None, ge=0)
|
||||
config: dict[str, Any] | None = None
|
||||
proxy: ProxyConfig | None = Field(None, description="代理配置")
|
||||
|
||||
# 复用验证器
|
||||
_validate_name = field_validator("name")(CreateEndpointRequest.validate_name.__func__)
|
||||
@@ -266,10 +266,10 @@ class CreateAPIKeyRequest(BaseModel):
|
||||
|
||||
endpoint_id: str = Field(..., description="Endpoint ID")
|
||||
api_key: str = Field(..., min_length=1, max_length=500, description="API Key")
|
||||
priority: Optional[int] = Field(100, ge=0, le=1000, description="优先级")
|
||||
is_active: Optional[bool] = Field(True, description="是否启用")
|
||||
rpm_limit: Optional[int] = Field(None, ge=0, description="RPM 限制(NULL=自适应)")
|
||||
notes: Optional[str] = Field(None, max_length=500, description="备注")
|
||||
priority: int | None = Field(100, ge=0, le=1000, description="优先级")
|
||||
is_active: bool | None = Field(True, description="是否启用")
|
||||
rpm_limit: int | None = Field(None, ge=0, description="RPM 限制(NULL=自适应)")
|
||||
notes: str | None = Field(None, max_length=500, description="备注")
|
||||
|
||||
@field_validator("api_key")
|
||||
@classmethod
|
||||
@@ -292,7 +292,7 @@ class CreateAPIKeyRequest(BaseModel):
|
||||
|
||||
@field_validator("notes")
|
||||
@classmethod
|
||||
def sanitize_notes(cls, v: Optional[str]) -> Optional[str]:
|
||||
def sanitize_notes(cls, v: str | None) -> str | None:
|
||||
"""清理备注"""
|
||||
if v is None:
|
||||
return v
|
||||
@@ -303,19 +303,19 @@ class CreateAPIKeyRequest(BaseModel):
|
||||
class UpdateUserRequest(BaseModel):
|
||||
"""更新用户请求"""
|
||||
|
||||
username: Optional[str] = Field(None, min_length=1, max_length=50)
|
||||
email: Optional[str] = Field(None, max_length=100)
|
||||
password: Optional[str] = Field(None, min_length=6, max_length=128, description="新密码(留空保持不变)")
|
||||
quota_usd: Optional[float] = Field(None, ge=0)
|
||||
is_active: Optional[bool] = None
|
||||
role: Optional[str] = None
|
||||
allowed_providers: Optional[List[str]] = Field(None, description="允许使用的提供商 ID 列表")
|
||||
allowed_api_formats: Optional[List[str]] = Field(None, description="允许使用的 API 格式列表")
|
||||
allowed_models: Optional[List[str]] = Field(None, description="允许使用的模型名称列表")
|
||||
username: str | None = Field(None, min_length=1, max_length=50)
|
||||
email: str | None = Field(None, max_length=100)
|
||||
password: str | None = Field(None, min_length=6, max_length=128, description="新密码(留空保持不变)")
|
||||
quota_usd: float | None = Field(None, ge=0)
|
||||
is_active: bool | None = None
|
||||
role: str | None = None
|
||||
allowed_providers: list[str] | None = Field(None, description="允许使用的提供商 ID 列表")
|
||||
allowed_api_formats: list[str] | None = Field(None, description="允许使用的 API 格式列表")
|
||||
allowed_models: list[str] | None = Field(None, description="允许使用的模型名称列表")
|
||||
|
||||
@field_validator("username")
|
||||
@classmethod
|
||||
def validate_username(cls, v: Optional[str]) -> Optional[str]:
|
||||
def validate_username(cls, v: str | None) -> str | None:
|
||||
"""验证用户名"""
|
||||
if v is None:
|
||||
return v
|
||||
@@ -327,7 +327,7 @@ class UpdateUserRequest(BaseModel):
|
||||
|
||||
@field_validator("email")
|
||||
@classmethod
|
||||
def validate_email(cls, v: Optional[str]) -> Optional[str]:
|
||||
def validate_email(cls, v: str | None) -> str | None:
|
||||
"""验证邮箱"""
|
||||
if v is None:
|
||||
return v
|
||||
@@ -341,7 +341,7 @@ class UpdateUserRequest(BaseModel):
|
||||
|
||||
@field_validator("role")
|
||||
@classmethod
|
||||
def validate_role(cls, v: Optional[str]) -> Optional[str]:
|
||||
def validate_role(cls, v: str | None) -> str | None:
|
||||
"""验证角色"""
|
||||
if v is None:
|
||||
return v
|
||||
|
||||
@@ -4,7 +4,7 @@ API端点请求/响应模型定义
|
||||
|
||||
import re
|
||||
from datetime import datetime
|
||||
from typing import Any, Dict, List, Literal, Optional
|
||||
from typing import Any, Literal
|
||||
|
||||
from pydantic import BaseModel, ConfigDict, Field, field_validator, model_validator
|
||||
|
||||
@@ -55,7 +55,7 @@ class LoginResponse(BaseModel):
|
||||
token_type: str = "bearer"
|
||||
expires_in: int = 86400 # Token有效期(秒),默认24小时
|
||||
user_id: str
|
||||
email: Optional[str] = None
|
||||
email: str | None = None
|
||||
username: str
|
||||
role: str
|
||||
|
||||
@@ -78,7 +78,7 @@ class RefreshTokenResponse(BaseModel):
|
||||
class RegisterRequest(BaseModel):
|
||||
"""注册请求"""
|
||||
|
||||
email: Optional[str] = Field(None, max_length=255, description="邮箱地址(可选)")
|
||||
email: str | None = Field(None, max_length=255, description="邮箱地址(可选)")
|
||||
username: str = Field(..., min_length=2, max_length=50, description="用户名")
|
||||
password: str = Field(..., min_length=6, max_length=128, description="密码")
|
||||
|
||||
@@ -126,7 +126,7 @@ class RegisterResponse(BaseModel):
|
||||
"""注册响应"""
|
||||
|
||||
user_id: str
|
||||
email: Optional[str] = None
|
||||
email: str | None = None
|
||||
username: str
|
||||
message: str
|
||||
|
||||
@@ -158,7 +158,7 @@ class SendVerificationCodeResponse(BaseModel):
|
||||
|
||||
message: str
|
||||
success: bool
|
||||
expire_minutes: Optional[int] = None
|
||||
expire_minutes: int | None = None
|
||||
|
||||
|
||||
class VerifyEmailRequest(BaseModel):
|
||||
@@ -219,8 +219,8 @@ class VerificationStatusResponse(BaseModel):
|
||||
email: str
|
||||
has_pending_code: bool = Field(description="是否有待验证的验证码")
|
||||
is_verified: bool = Field(description="邮箱是否已验证")
|
||||
cooldown_remaining: Optional[int] = Field(None, description="发送冷却剩余秒数")
|
||||
code_expires_in: Optional[int] = Field(None, description="验证码剩余有效秒数")
|
||||
cooldown_remaining: int | None = Field(None, description="发送冷却剩余秒数")
|
||||
code_expires_in: int | None = Field(None, description="验证码剩余有效秒数")
|
||||
|
||||
|
||||
class RegistrationSettingsResponse(BaseModel):
|
||||
@@ -237,14 +237,14 @@ class CreateUserRequest(BaseModel):
|
||||
|
||||
username: str = Field(..., min_length=2, max_length=50, description="用户名")
|
||||
password: str = Field(..., min_length=6, max_length=128, description="密码")
|
||||
email: Optional[str] = Field(None, max_length=255, description="邮箱地址(可选)")
|
||||
role: Optional[UserRole] = Field(UserRole.USER, description="用户角色")
|
||||
quota_usd: Optional[float] = Field(default=None, description="USD配额,null表示使用系统默认配额")
|
||||
email: str | None = Field(None, max_length=255, description="邮箱地址(可选)")
|
||||
role: UserRole | None = Field(UserRole.USER, description="用户角色")
|
||||
quota_usd: float | None = Field(default=None, description="USD配额,null表示使用系统默认配额")
|
||||
unlimited: bool = Field(default=False, description="是否无限配额")
|
||||
# 访问限制字段
|
||||
allowed_providers: Optional[List[str]] = Field(default=None, description="允许使用的提供商ID列表,null表示无限制")
|
||||
allowed_api_formats: Optional[List[str]] = Field(default=None, description="允许使用的API格式列表,null表示无限制")
|
||||
allowed_models: Optional[List[str]] = Field(default=None, description="允许使用的模型名称列表,null表示无限制")
|
||||
allowed_providers: list[str] | None = Field(default=None, description="允许使用的提供商ID列表,null表示无限制")
|
||||
allowed_api_formats: list[str] | None = Field(default=None, description="允许使用的API格式列表,null表示无限制")
|
||||
allowed_models: list[str] | None = Field(default=None, description="允许使用的模型名称列表,null表示无限制")
|
||||
|
||||
@field_validator("quota_usd", mode="before")
|
||||
@classmethod
|
||||
@@ -260,7 +260,7 @@ class CreateUserRequest(BaseModel):
|
||||
|
||||
@field_validator("email")
|
||||
@classmethod
|
||||
def validate_email(cls, v: Optional[str]) -> Optional[str]:
|
||||
def validate_email(cls, v: str | None) -> str | None:
|
||||
"""验证邮箱格式(如果提供)"""
|
||||
if v is None:
|
||||
return None
|
||||
@@ -301,15 +301,15 @@ class CreateUserRequest(BaseModel):
|
||||
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_api_formats: Optional[List[str]] = None # 允许使用的 API 格式列表
|
||||
allowed_models: Optional[List[str]] = None # 允许使用的模型名称列表
|
||||
quota_usd: Optional[float] = None
|
||||
is_active: Optional[bool] = None
|
||||
email: str | None = None
|
||||
username: str | None = None
|
||||
password: str | None = None
|
||||
role: UserRole | None = None
|
||||
allowed_providers: list[str] | None = None # 允许使用的提供商 ID 列表
|
||||
allowed_api_formats: list[str] | None = None # 允许使用的 API 格式列表
|
||||
allowed_models: list[str] | None = None # 允许使用的模型名称列表
|
||||
quota_usd: float | None = None
|
||||
is_active: bool | None = None
|
||||
|
||||
@field_validator("quota_usd", mode="before")
|
||||
@classmethod
|
||||
@@ -327,14 +327,14 @@ class UpdateUserRequest(BaseModel):
|
||||
class CreateApiKeyRequest(BaseModel):
|
||||
"""创建API密钥请求"""
|
||||
|
||||
name: Optional[str] = None
|
||||
allowed_providers: Optional[List[str]] = None # 允许使用的提供商 ID 列表
|
||||
allowed_api_formats: Optional[List[str]] = None # 允许使用的 API 格式列表
|
||||
allowed_models: Optional[List[str]] = None # 允许使用的模型名称列表
|
||||
rate_limit: Optional[int] = None # None = 无限制
|
||||
expire_days: Optional[int] = None # None = 永不过期,数字 = 多少天后过期(兼容旧版)
|
||||
expires_at: Optional[str] = None # ISO 日期字符串,如 "2025-12-31",优先于 expire_days
|
||||
initial_balance_usd: Optional[float] = Field(
|
||||
name: str | None = None
|
||||
allowed_providers: list[str] | None = None # 允许使用的提供商 ID 列表
|
||||
allowed_api_formats: list[str] | None = None # 允许使用的 API 格式列表
|
||||
allowed_models: list[str] | None = None # 允许使用的模型名称列表
|
||||
rate_limit: int | None = None # None = 无限制
|
||||
expire_days: int | None = None # None = 永不过期,数字 = 多少天后过期(兼容旧版)
|
||||
expires_at: str | None = None # ISO 日期字符串,如 "2025-12-31",优先于 expire_days
|
||||
initial_balance_usd: float | None = Field(
|
||||
None, description="初始余额(USD),仅用于独立Key,None = 无限制"
|
||||
)
|
||||
is_standalone: bool = Field(False, description="是否为独立余额Key(给非注册用户使用)")
|
||||
@@ -347,18 +347,18 @@ class UserResponse(BaseModel):
|
||||
"""用户响应"""
|
||||
|
||||
id: str
|
||||
email: Optional[str] = None
|
||||
email: str | None = None
|
||||
username: str
|
||||
role: UserRole
|
||||
allowed_providers: Optional[List[str]] = None # 允许使用的提供商 ID 列表
|
||||
allowed_api_formats: Optional[List[str]] = None # 允许使用的 API 格式列表
|
||||
allowed_models: Optional[List[str]] = None # 允许使用的模型名称列表
|
||||
allowed_providers: list[str] | None = None # 允许使用的提供商 ID 列表
|
||||
allowed_api_formats: list[str] | None = None # 允许使用的 API 格式列表
|
||||
allowed_models: list[str] | None = None # 允许使用的模型名称列表
|
||||
quota_usd: float
|
||||
used_usd: float
|
||||
is_active: bool
|
||||
created_at: datetime
|
||||
updated_at: datetime
|
||||
last_login_at: Optional[datetime]
|
||||
last_login_at: datetime | None
|
||||
|
||||
|
||||
class ApiKeyResponse(BaseModel):
|
||||
@@ -366,23 +366,23 @@ class ApiKeyResponse(BaseModel):
|
||||
|
||||
id: str
|
||||
user_id: str
|
||||
key: Optional[str] = None # 仅在创建时返回完整密钥
|
||||
key_display: Optional[str] = None # 脱敏后的密钥显示
|
||||
name: Optional[str]
|
||||
key: str | None = None # 仅在创建时返回完整密钥
|
||||
key_display: str | None = None # 脱敏后的密钥显示
|
||||
name: str | None
|
||||
total_requests: int
|
||||
total_tokens: int
|
||||
total_cost_usd: float
|
||||
allowed_providers: Optional[List[str]]
|
||||
allowed_models: Optional[List[str]]
|
||||
allowed_providers: list[str] | None
|
||||
allowed_models: list[str] | None
|
||||
rate_limit: int
|
||||
is_active: bool
|
||||
expires_at: Optional[datetime] = None
|
||||
expires_at: datetime | None = None
|
||||
balance_used_usd: float = 0.0
|
||||
current_balance_usd: Optional[float] = None # NULL = 无限制
|
||||
current_balance_usd: float | None = None # NULL = 无限制
|
||||
is_standalone: bool = False
|
||||
force_capabilities: Optional[Dict[str, bool]] = None # 强制开启的能力
|
||||
force_capabilities: dict[str, bool] | None = None # 强制开启的能力
|
||||
created_at: datetime
|
||||
last_used_at: Optional[datetime]
|
||||
last_used_at: datetime | None
|
||||
|
||||
|
||||
# ========== 提供商管理 ==========
|
||||
@@ -396,40 +396,40 @@ class ProviderCreate(BaseModel):
|
||||
"""
|
||||
|
||||
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="主站网站")
|
||||
description: str | None = Field(None, description="提供商描述")
|
||||
website: str | None = 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="额外配置")
|
||||
rate_limit: int | None = Field(None, description="每分钟请求限制")
|
||||
concurrent_limit: int | None = Field(None, description="并发请求限制")
|
||||
config: dict | None = Field(None, description="额外配置")
|
||||
is_active: bool = Field(False, description="是否启用(默认false,需要配置API密钥后才能启用)")
|
||||
|
||||
# 超时配置(秒),为空时使用全局配置
|
||||
stream_first_byte_timeout: Optional[float] = Field(None, ge=1, le=300, description="流式请求首字节超时(秒)")
|
||||
request_timeout: Optional[float] = Field(None, ge=1, le=600, description="非流式请求整体超时(秒)")
|
||||
stream_first_byte_timeout: float | None = Field(None, ge=1, le=300, description="流式请求首字节超时(秒)")
|
||||
request_timeout: float | None = Field(None, ge=1, le=600, description="非流式请求整体超时(秒)")
|
||||
|
||||
|
||||
class ProviderUpdate(BaseModel):
|
||||
"""更新提供商请求"""
|
||||
|
||||
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
|
||||
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
|
||||
name: str | None = Field(None, min_length=1, max_length=100)
|
||||
description: str | None = None
|
||||
website: str | None = Field(None, max_length=500)
|
||||
api_format: str | None = None
|
||||
base_url: str | None = None
|
||||
headers: dict | None = None
|
||||
max_retries: int | None = Field(None, ge=0, le=10)
|
||||
priority: int | None = None
|
||||
weight: float | None = Field(None, gt=0)
|
||||
rate_limit: int | None = None
|
||||
concurrent_limit: int | None = None
|
||||
config: dict | None = None
|
||||
is_active: bool | None = None
|
||||
|
||||
# 超时配置(秒),为空时使用全局配置
|
||||
stream_first_byte_timeout: Optional[float] = Field(None, ge=1, le=300, description="流式请求首字节超时(秒)")
|
||||
request_timeout: Optional[float] = Field(None, ge=1, le=600, description="非流式请求整体超时(秒)")
|
||||
stream_first_byte_timeout: float | None = Field(None, ge=1, le=300, description="流式请求首字节超时(秒)")
|
||||
request_timeout: float | None = Field(None, ge=1, le=600, description="非流式请求整体超时(秒)")
|
||||
|
||||
|
||||
class ProviderResponse(BaseModel):
|
||||
@@ -437,17 +437,17 @@ class ProviderResponse(BaseModel):
|
||||
|
||||
id: str
|
||||
name: str
|
||||
description: Optional[str]
|
||||
website: Optional[str]
|
||||
description: str | None
|
||||
website: str | None
|
||||
api_format: str
|
||||
base_url: str
|
||||
headers: Optional[dict]
|
||||
headers: dict | None
|
||||
max_retries: int
|
||||
priority: int
|
||||
weight: float
|
||||
rate_limit: Optional[int]
|
||||
concurrent_limit: Optional[int]
|
||||
config: Optional[dict]
|
||||
rate_limit: int | None
|
||||
concurrent_limit: int | None
|
||||
config: dict | None
|
||||
is_active: bool
|
||||
created_at: datetime
|
||||
updated_at: datetime
|
||||
@@ -456,8 +456,8 @@ class ProviderResponse(BaseModel):
|
||||
api_keys_count: int = 0
|
||||
|
||||
# 超时配置
|
||||
stream_first_byte_timeout: Optional[float] = None
|
||||
request_timeout: Optional[float] = None
|
||||
stream_first_byte_timeout: float | None = None
|
||||
request_timeout: float | None = None
|
||||
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
@@ -469,52 +469,52 @@ class ModelCreate(BaseModel):
|
||||
provider_model_name: str = Field(
|
||||
..., min_length=1, max_length=200, description="Provider 侧的主模型名称"
|
||||
)
|
||||
provider_model_mappings: Optional[List[dict]] = Field(
|
||||
provider_model_mappings: list[dict] | None = Field(
|
||||
None,
|
||||
description="模型名称映射列表,格式: [{'name': 'alias1', 'priority': 1}, ...]",
|
||||
)
|
||||
global_model_id: str = Field(..., description="关联的 GlobalModel ID(必填)")
|
||||
# 按次计费配置 - 可选,为空时使用 GlobalModel 默认值
|
||||
price_per_request: Optional[float] = Field(
|
||||
price_per_request: float | None = Field(
|
||||
None, ge=0, description="每次请求固定费用,为空使用默认值"
|
||||
)
|
||||
# 阶梯计费配置 - 可选,为空时使用 GlobalModel 默认值
|
||||
tiered_pricing: Optional[dict] = Field(
|
||||
tiered_pricing: dict | None = Field(
|
||||
None, description="阶梯计费配置,为空使用 GlobalModel 默认值"
|
||||
)
|
||||
# 能力配置 - 可选,为空时使用 GlobalModel 默认值
|
||||
supports_vision: Optional[bool] = Field(None, description="是否支持图像输入,为空使用默认值")
|
||||
supports_function_calling: Optional[bool] = Field(
|
||||
supports_vision: bool | None = Field(None, description="是否支持图像输入,为空使用默认值")
|
||||
supports_function_calling: bool | None = Field(
|
||||
None, description="是否支持函数调用,为空使用默认值"
|
||||
)
|
||||
supports_streaming: Optional[bool] = Field(None, description="是否支持流式输出,为空使用默认值")
|
||||
supports_extended_thinking: Optional[bool] = Field(
|
||||
supports_streaming: bool | None = Field(None, description="是否支持流式输出,为空使用默认值")
|
||||
supports_extended_thinking: bool | None = Field(
|
||||
None, description="是否支持扩展思考,为空使用默认值"
|
||||
)
|
||||
is_active: bool = Field(True, description="是否启用")
|
||||
config: Optional[dict] = Field(None, description="额外配置")
|
||||
config: dict | None = Field(None, description="额外配置")
|
||||
|
||||
|
||||
class ModelUpdate(BaseModel):
|
||||
"""更新模型请求"""
|
||||
|
||||
provider_model_name: Optional[str] = Field(None, min_length=1, max_length=200)
|
||||
provider_model_mappings: Optional[List[dict]] = Field(
|
||||
provider_model_name: str | None = Field(None, min_length=1, max_length=200)
|
||||
provider_model_mappings: list[dict] | None = Field(
|
||||
None,
|
||||
description="模型名称映射列表,格式: [{'name': 'alias1', 'priority': 1}, ...]",
|
||||
)
|
||||
global_model_id: Optional[str] = None
|
||||
global_model_id: str | None = None
|
||||
# 按次计费配置
|
||||
price_per_request: Optional[float] = Field(None, ge=0, description="每次请求固定费用")
|
||||
price_per_request: float | None = 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
|
||||
tiered_pricing: dict | None = Field(None, description="阶梯计费配置")
|
||||
supports_vision: bool | None = None
|
||||
supports_function_calling: bool | None = None
|
||||
supports_streaming: bool | None = None
|
||||
supports_extended_thinking: bool | None = None
|
||||
is_active: bool | None = None
|
||||
is_available: bool | None = None
|
||||
config: dict | None = None
|
||||
|
||||
|
||||
class ModelResponse(BaseModel):
|
||||
@@ -525,32 +525,32 @@ class ModelResponse(BaseModel):
|
||||
|
||||
id: str
|
||||
provider_id: str
|
||||
global_model_id: Optional[str]
|
||||
global_model_id: str | None
|
||||
provider_model_name: str
|
||||
provider_model_mappings: Optional[List[dict]] = None
|
||||
provider_model_mappings: list[dict] | None = None
|
||||
|
||||
# 按次计费配置
|
||||
price_per_request: Optional[float] = None
|
||||
price_per_request: float | None = None
|
||||
# 阶梯计费配置
|
||||
tiered_pricing: Optional[dict] = None
|
||||
tiered_pricing: dict | None = 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]
|
||||
supports_vision: bool | None
|
||||
supports_function_calling: bool | None
|
||||
supports_streaming: bool | None
|
||||
supports_extended_thinking: bool | None
|
||||
supports_image_generation: bool | None
|
||||
|
||||
# 有效值(合并 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
|
||||
effective_tiered_pricing: dict | None = None
|
||||
effective_input_price: float | None = None
|
||||
effective_output_price: float | None = None
|
||||
effective_price_per_request: float | None = None
|
||||
effective_supports_vision: bool | None = None
|
||||
effective_supports_function_calling: bool | None = None
|
||||
effective_supports_streaming: bool | None = None
|
||||
effective_supports_extended_thinking: bool | None = None
|
||||
effective_supports_image_generation: bool | None = None
|
||||
|
||||
# 状态
|
||||
is_active: bool
|
||||
@@ -561,8 +561,8 @@ class ModelResponse(BaseModel):
|
||||
updated_at: datetime
|
||||
|
||||
# 关联的 GlobalModel 信息(如果有)
|
||||
global_model_name: Optional[str] = None
|
||||
global_model_display_name: Optional[str] = None
|
||||
global_model_name: str | None = None
|
||||
global_model_display_name: str | None = None
|
||||
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
@@ -574,19 +574,19 @@ class ModelDetailResponse(BaseModel):
|
||||
provider_id: str
|
||||
name: str
|
||||
display_name: str
|
||||
description: Optional[str]
|
||||
icon_url: Optional[str]
|
||||
tags: Optional[List[str]]
|
||||
description: str | None
|
||||
icon_url: str | None
|
||||
tags: list[str] | None
|
||||
input_price_per_1m: float
|
||||
output_price_per_1m: float
|
||||
cache_creation_price_per_1m: Optional[float]
|
||||
cache_read_price_per_1m: Optional[float]
|
||||
cache_creation_price_per_1m: float | None
|
||||
cache_read_price_per_1m: float | None
|
||||
supports_vision: bool
|
||||
supports_function_calling: bool
|
||||
supports_streaming: bool
|
||||
is_active: bool
|
||||
is_available: bool
|
||||
config: Optional[dict]
|
||||
config: dict | None
|
||||
created_at: datetime
|
||||
updated_at: datetime
|
||||
|
||||
@@ -597,16 +597,16 @@ class ModelDetailResponse(BaseModel):
|
||||
class SystemSettingsRequest(BaseModel):
|
||||
"""系统设置请求"""
|
||||
|
||||
default_provider: Optional[str] = None
|
||||
default_model: Optional[str] = None
|
||||
enable_usage_tracking: Optional[bool] = None
|
||||
default_provider: str | None = None
|
||||
default_model: str | None = None
|
||||
enable_usage_tracking: bool | None = None
|
||||
|
||||
|
||||
class SystemSettingsResponse(BaseModel):
|
||||
"""系统设置响应"""
|
||||
|
||||
default_provider: Optional[str]
|
||||
default_model: Optional[str]
|
||||
default_provider: str | None
|
||||
default_model: str | None
|
||||
enable_usage_tracking: bool
|
||||
|
||||
|
||||
@@ -620,8 +620,8 @@ class UsageStatsResponse(BaseModel):
|
||||
daily_requests: int
|
||||
daily_tokens: int
|
||||
daily_cost_usd: float
|
||||
model_usage: Dict[str, Dict[str, Any]]
|
||||
provider_usage: Dict[str, Dict[str, Any]]
|
||||
model_usage: dict[str, dict[str, Any]]
|
||||
provider_usage: dict[str, dict[str, Any]]
|
||||
|
||||
|
||||
# ========== 公开API响应模型 ==========
|
||||
@@ -630,8 +630,8 @@ class PublicProviderResponse(BaseModel):
|
||||
|
||||
id: str
|
||||
name: str
|
||||
description: Optional[str]
|
||||
website: Optional[str]
|
||||
description: str | None
|
||||
website: str | None
|
||||
is_active: bool
|
||||
provider_priority: int # 提供商优先级(数字越小越优先)
|
||||
# 统计信息
|
||||
@@ -649,18 +649,18 @@ class PublicModelResponse(BaseModel):
|
||||
provider_name: str
|
||||
name: str
|
||||
display_name: str
|
||||
description: Optional[str] = None
|
||||
tags: Optional[List[str]] = None
|
||||
icon_url: Optional[str] = None
|
||||
description: str | None = None
|
||||
tags: list[str] | None = None
|
||||
icon_url: str | None = 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
|
||||
input_price_per_1m: float | None = None
|
||||
output_price_per_1m: float | None = None
|
||||
cache_creation_price_per_1m: float | None = None
|
||||
cache_read_price_per_1m: float | None = None
|
||||
# 功能支持
|
||||
supports_vision: Optional[bool] = None
|
||||
supports_function_calling: Optional[bool] = None
|
||||
supports_streaming: Optional[bool] = None
|
||||
supports_vision: bool | None = None
|
||||
supports_function_calling: bool | None = None
|
||||
supports_streaming: bool | None = None
|
||||
is_active: bool = True
|
||||
|
||||
|
||||
@@ -671,7 +671,7 @@ class ProviderStatsResponse(BaseModel):
|
||||
active_providers: int
|
||||
total_models: int
|
||||
active_models: int
|
||||
supported_formats: List[str]
|
||||
supported_formats: list[str]
|
||||
|
||||
|
||||
class PublicGlobalModelResponse(BaseModel):
|
||||
@@ -679,22 +679,22 @@ class PublicGlobalModelResponse(BaseModel):
|
||||
|
||||
id: str
|
||||
name: str
|
||||
display_name: Optional[str] = None
|
||||
display_name: str | None = None
|
||||
is_active: bool = True
|
||||
# 按次计费配置
|
||||
default_price_per_request: Optional[float] = None
|
||||
default_price_per_request: float | None = None
|
||||
# 阶梯计费配置
|
||||
default_tiered_pricing: Optional[dict] = None
|
||||
default_tiered_pricing: dict | None = None
|
||||
# Key 能力配置
|
||||
supported_capabilities: Optional[List[str]] = None
|
||||
supported_capabilities: list[str] | None = None
|
||||
# 模型配置(JSON)
|
||||
config: Optional[dict] = None
|
||||
config: dict | None = None
|
||||
|
||||
|
||||
class PublicGlobalModelListResponse(BaseModel):
|
||||
"""公开的 GlobalModel 列表响应"""
|
||||
|
||||
models: List[PublicGlobalModelResponse]
|
||||
models: list[PublicGlobalModelResponse]
|
||||
total: int
|
||||
|
||||
|
||||
@@ -702,28 +702,28 @@ class PublicGlobalModelListResponse(BaseModel):
|
||||
class UpdateProfileRequest(BaseModel):
|
||||
"""更新个人信息请求"""
|
||||
|
||||
email: Optional[str] = None
|
||||
username: Optional[str] = None
|
||||
email: str | None = None
|
||||
username: str | None = 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
|
||||
avatar_url: str | None = None
|
||||
bio: str | None = None
|
||||
default_provider_id: int | None = None
|
||||
theme: str | None = None
|
||||
language: str | None = None
|
||||
timezone: str | None = None
|
||||
email_notifications: bool | None = None
|
||||
usage_alerts: bool | None = None
|
||||
announcement_notifications: bool | None = None
|
||||
|
||||
|
||||
class ChangePasswordRequest(BaseModel):
|
||||
"""修改密码请求"""
|
||||
|
||||
old_password: Optional[str] = None # 可选:首次设置密码时不需要
|
||||
old_password: str | None = None # 可选:首次设置密码时不需要
|
||||
new_password: str
|
||||
|
||||
|
||||
@@ -745,7 +745,7 @@ class ProviderConfig(BaseModel):
|
||||
class UpdateApiKeyProvidersRequest(BaseModel):
|
||||
"""更新API密钥可用提供商请求"""
|
||||
|
||||
allowed_providers: Optional[List[ProviderConfig]] = None # 提供商配置列表
|
||||
allowed_providers: list[ProviderConfig] | None = None # 提供商配置列表
|
||||
|
||||
|
||||
# ========== 公告相关模型 ==========
|
||||
@@ -757,18 +757,18 @@ class CreateAnnouncementRequest(BaseModel):
|
||||
type: str = "info" # info, warning, maintenance, important
|
||||
priority: int = 0
|
||||
is_pinned: bool = False
|
||||
start_time: Optional[datetime] = None
|
||||
end_time: Optional[datetime] = None
|
||||
start_time: datetime | None = None
|
||||
end_time: datetime | None = 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
|
||||
title: str | None = None
|
||||
content: str | None = None
|
||||
type: str | None = None
|
||||
priority: int | None = None
|
||||
is_active: bool | None = None
|
||||
is_pinned: bool | None = None
|
||||
start_time: datetime | None = None
|
||||
end_time: datetime | None = None
|
||||
|
||||
@@ -3,7 +3,6 @@ Provider API Key相关的API模型
|
||||
"""
|
||||
|
||||
from datetime import datetime
|
||||
from typing import Optional
|
||||
|
||||
from pydantic import BaseModel, ConfigDict, Field
|
||||
|
||||
@@ -11,12 +10,12 @@ from pydantic import BaseModel, ConfigDict, Field
|
||||
class ProviderAPIKeyBase(BaseModel):
|
||||
"""Provider API Key基础模型"""
|
||||
|
||||
name: Optional[str] = Field(None, description="密钥名称/备注")
|
||||
name: str | None = Field(None, description="密钥名称/备注")
|
||||
api_key: str = Field(..., description="API密钥")
|
||||
rpm_limit: Optional[int] = Field(None, description="RPM限制(每分钟请求数),NULL=自适应模式")
|
||||
rpm_limit: int | None = Field(None, description="RPM限制(每分钟请求数),NULL=自适应模式")
|
||||
priority: int = Field(0, description="优先级(越高越优先使用)")
|
||||
is_active: bool = Field(True, description="是否启用")
|
||||
expires_at: Optional[datetime] = Field(None, description="过期时间")
|
||||
expires_at: datetime | None = Field(None, description="过期时间")
|
||||
|
||||
|
||||
class ProviderAPIKeyCreate(ProviderAPIKeyBase):
|
||||
@@ -28,12 +27,12 @@ class ProviderAPIKeyCreate(ProviderAPIKeyBase):
|
||||
class ProviderAPIKeyUpdate(BaseModel):
|
||||
"""更新Provider API Key请求"""
|
||||
|
||||
name: Optional[str] = None
|
||||
api_key: Optional[str] = None
|
||||
rpm_limit: Optional[int] = None
|
||||
priority: Optional[int] = None
|
||||
is_active: Optional[bool] = None
|
||||
expires_at: Optional[datetime] = None
|
||||
name: str | None = None
|
||||
api_key: str | None = None
|
||||
rpm_limit: int | None = None
|
||||
priority: int | None = None
|
||||
is_active: bool | None = None
|
||||
expires_at: datetime | None = None
|
||||
|
||||
|
||||
class ProviderAPIKeyResponse(ProviderAPIKeyBase):
|
||||
@@ -41,11 +40,11 @@ class ProviderAPIKeyResponse(ProviderAPIKeyBase):
|
||||
|
||||
id: str
|
||||
provider_id: str
|
||||
request_count: Optional[int] = Field(0, description="请求次数")
|
||||
error_count: Optional[int] = Field(0, description="错误次数")
|
||||
last_used_at: Optional[datetime] = Field(None, description="最后使用时间")
|
||||
last_error_at: Optional[datetime] = Field(None, description="最后错误时间")
|
||||
last_error_msg: Optional[str] = Field(None, description="最后错误信息")
|
||||
request_count: int | None = Field(0, description="请求次数")
|
||||
error_count: int | None = Field(0, description="错误次数")
|
||||
last_used_at: datetime | None = Field(None, description="最后使用时间")
|
||||
last_error_at: datetime | None = Field(None, description="最后错误时间")
|
||||
last_error_msg: str | None = Field(None, description="最后错误信息")
|
||||
created_at: datetime
|
||||
updated_at: datetime
|
||||
|
||||
@@ -56,10 +55,10 @@ class ProviderAPIKeyStats(BaseModel):
|
||||
"""Provider API Key统计信息"""
|
||||
|
||||
id: str
|
||||
name: Optional[str]
|
||||
name: str | None
|
||||
request_count: int
|
||||
error_count: int
|
||||
success_rate: float
|
||||
last_used_at: Optional[datetime]
|
||||
last_used_at: datetime | None
|
||||
is_active: bool
|
||||
is_expired: bool
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
from typing import Any, Dict, List, Literal, Optional, Union
|
||||
from typing import Any, Literal
|
||||
|
||||
from pydantic import BaseModel, ConfigDict, Field
|
||||
from pydantic import BaseModel, ConfigDict
|
||||
|
||||
|
||||
# 配置允许额外字段,以支持API的新特性
|
||||
@@ -15,20 +15,20 @@ class ClaudeContentBlockText(BaseModelWithExtras):
|
||||
|
||||
class ClaudeContentBlockImage(BaseModelWithExtras):
|
||||
type: Literal["image"]
|
||||
source: Dict[str, Any]
|
||||
source: dict[str, Any]
|
||||
|
||||
|
||||
class ClaudeContentBlockToolUse(BaseModelWithExtras):
|
||||
type: Literal["tool_use"]
|
||||
id: str
|
||||
name: str
|
||||
input: Dict[str, Any]
|
||||
input: dict[str, Any]
|
||||
|
||||
|
||||
class ClaudeContentBlockToolResult(BaseModelWithExtras):
|
||||
type: Literal["tool_result"]
|
||||
tool_use_id: str
|
||||
content: Union[str, List[Dict[str, Any]], Dict[str, Any]]
|
||||
content: str | list[dict[str, Any]] | dict[str, Any]
|
||||
|
||||
|
||||
class ClaudeContentBlockThinking(BaseModelWithExtras):
|
||||
@@ -45,13 +45,13 @@ class ClaudeMessage(BaseModelWithExtras):
|
||||
role: Literal["user", "assistant"]
|
||||
# 宽松的内容类型定义 - 接受字符串或任意字典列表
|
||||
# 作为转发代理,不应该严格限制内容块类型,以支持API的新特性
|
||||
content: Union[str, List[Dict[str, Any]]]
|
||||
content: str | list[dict[str, Any]]
|
||||
|
||||
|
||||
class ClaudeTool(BaseModelWithExtras):
|
||||
name: str
|
||||
description: Optional[str] = None
|
||||
input_schema: Dict[str, Any]
|
||||
description: str | None = None
|
||||
input_schema: dict[str, Any]
|
||||
|
||||
|
||||
class ClaudeThinkingConfig(BaseModelWithExtras):
|
||||
@@ -61,28 +61,28 @@ class ClaudeThinkingConfig(BaseModelWithExtras):
|
||||
class ClaudeMessagesRequest(BaseModelWithExtras):
|
||||
model: str
|
||||
max_tokens: int
|
||||
messages: List[ClaudeMessage]
|
||||
messages: list[ClaudeMessage]
|
||||
# 宽松的system类型 - 接受字符串、字典列表或任意字典
|
||||
system: Optional[Union[str, List[Dict[str, Any]], Dict[str, Any]]] = None
|
||||
stop_sequences: Optional[List[str]] = None
|
||||
stream: Optional[bool] = False
|
||||
temperature: Optional[float] = 1.0
|
||||
top_p: Optional[float] = None
|
||||
top_k: Optional[int] = None
|
||||
metadata: Optional[Dict[str, Any]] = None
|
||||
tools: Optional[List[Dict[str, Any]]] = None # 改为更宽松的类型
|
||||
tool_choice: Optional[Dict[str, Any]] = None
|
||||
thinking: Optional[Dict[str, Any]] = None # 改为更宽松的类型
|
||||
system: str | list[dict[str, Any]] | dict[str, Any] | None = None
|
||||
stop_sequences: list[str] | None = None
|
||||
stream: bool | None = False
|
||||
temperature: float | None = 1.0
|
||||
top_p: float | None = None
|
||||
top_k: int | None = None
|
||||
metadata: dict[str, Any] | None = None
|
||||
tools: list[dict[str, Any]] | None = None # 改为更宽松的类型
|
||||
tool_choice: dict[str, Any] | None = None
|
||||
thinking: dict[str, Any] | None = None # 改为更宽松的类型
|
||||
|
||||
|
||||
class ClaudeTokenCountRequest(BaseModelWithExtras):
|
||||
model: str
|
||||
messages: List[ClaudeMessage]
|
||||
messages: list[ClaudeMessage]
|
||||
# 宽松的类型定义以支持API新特性
|
||||
system: Optional[Union[str, List[Dict[str, Any]], Dict[str, Any]]] = None
|
||||
tools: Optional[List[Dict[str, Any]]] = None
|
||||
thinking: Optional[Dict[str, Any]] = None
|
||||
tool_choice: Optional[Dict[str, Any]] = None
|
||||
system: str | list[dict[str, Any]] | dict[str, Any] | None = None
|
||||
tools: list[dict[str, Any]] | None = None
|
||||
thinking: dict[str, Any] | None = None
|
||||
tool_choice: dict[str, Any] | None = None
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
@@ -95,8 +95,8 @@ class ClaudeResponseUsage(BaseModelWithExtras):
|
||||
|
||||
input_tokens: int = 0
|
||||
output_tokens: int = 0
|
||||
cache_creation_input_tokens: Optional[int] = None
|
||||
cache_read_input_tokens: Optional[int] = None
|
||||
cache_creation_input_tokens: int | None = None
|
||||
cache_read_input_tokens: int | None = None
|
||||
|
||||
|
||||
class ClaudeResponse(BaseModelWithExtras):
|
||||
@@ -110,9 +110,9 @@ class ClaudeResponse(BaseModelWithExtras):
|
||||
model: str
|
||||
type: Literal["message"] = "message"
|
||||
role: Literal["assistant"] = "assistant"
|
||||
content: List[Dict[str, Any]]
|
||||
stop_reason: Optional[str] = None
|
||||
stop_sequence: Optional[str] = None
|
||||
usage: Optional[ClaudeResponseUsage] = None
|
||||
context_management: Optional[Dict[str, Any]] = None
|
||||
container: Optional[Dict[str, Any]] = None
|
||||
content: list[dict[str, Any]]
|
||||
stop_reason: str | None = None
|
||||
stop_sequence: str | None = None
|
||||
usage: ClaudeResponseUsage | None = None
|
||||
context_management: dict[str, Any] | None = None
|
||||
container: dict[str, Any] | None = None
|
||||
|
||||
@@ -7,7 +7,6 @@ import secrets
|
||||
import uuid
|
||||
from datetime import datetime, timezone
|
||||
from enum import Enum as PyEnum
|
||||
from typing import Optional
|
||||
|
||||
import bcrypt
|
||||
from sqlalchemy import (
|
||||
@@ -1008,7 +1007,7 @@ class Model(Base):
|
||||
return self._get_effective_capability("supports_image_generation", False)
|
||||
|
||||
def select_provider_model_name(
|
||||
self, affinity_key: Optional[str] = None, api_format: Optional[str] = None
|
||||
self, affinity_key: str | None = None, api_format: str | None = None
|
||||
) -> str:
|
||||
"""按优先级选择要使用的 Provider 模型名称
|
||||
|
||||
@@ -1868,4 +1867,3 @@ class StatsUserDaily(Base):
|
||||
|
||||
|
||||
# 导入扩展的数据库模型
|
||||
from .database_extensions import ApiKeyProviderMapping, ProviderUsageTracking
|
||||
|
||||
@@ -14,7 +14,6 @@ from sqlalchemy import (
|
||||
Index,
|
||||
Integer,
|
||||
String,
|
||||
Text,
|
||||
UniqueConstraint,
|
||||
)
|
||||
from sqlalchemy.orm import relationship
|
||||
|
||||
@@ -4,7 +4,7 @@ ProviderEndpoint 相关的 API 模型定义
|
||||
|
||||
import re
|
||||
from datetime import datetime
|
||||
from typing import Any, Dict, List, Optional
|
||||
from typing import Any
|
||||
|
||||
from pydantic import BaseModel, ConfigDict, Field, field_validator
|
||||
|
||||
@@ -17,7 +17,7 @@ from src.models.admin_requests import ProxyConfig
|
||||
# - drop: 删除请求头 {"action": "drop", "key": "X-Unwanted"}
|
||||
# - rename: 重命名请求头 {"action": "rename", "from": "X-Old", "to": "X-New"}
|
||||
# 实际验证在 headers.py 的 apply_rules 中处理
|
||||
HeaderRule = Dict[str, Any]
|
||||
HeaderRule = dict[str, Any]
|
||||
|
||||
|
||||
# ========== ProviderEndpoint CRUD ==========
|
||||
@@ -29,10 +29,10 @@ class ProviderEndpointCreate(BaseModel):
|
||||
provider_id: str = Field(..., description="Provider ID")
|
||||
api_format: str = Field(..., description="API 格式 (CLAUDE, OPENAI, CLAUDE_CLI, OPENAI_CLI)")
|
||||
base_url: str = Field(..., min_length=1, max_length=500, description="API 基础 URL")
|
||||
custom_path: Optional[str] = Field(default=None, max_length=200, description="自定义请求路径")
|
||||
custom_path: str | None = Field(default=None, max_length=200, description="自定义请求路径")
|
||||
|
||||
# 请求头配置
|
||||
header_rules: Optional[List[HeaderRule]] = Field(
|
||||
header_rules: list[HeaderRule] | None = Field(
|
||||
default=None,
|
||||
description="请求头规则列表,支持 set/drop/rename 操作",
|
||||
)
|
||||
@@ -40,13 +40,13 @@ class ProviderEndpointCreate(BaseModel):
|
||||
max_retries: int = Field(default=2, ge=0, le=10, description="最大重试次数")
|
||||
|
||||
# 额外配置
|
||||
config: Optional[Dict[str, Any]] = Field(default=None, description="额外配置(JSON)")
|
||||
config: dict[str, Any] | None = Field(default=None, description="额外配置(JSON)")
|
||||
|
||||
# 代理配置
|
||||
proxy: Optional[ProxyConfig] = Field(default=None, description="代理配置")
|
||||
proxy: ProxyConfig | None = Field(default=None, description="代理配置")
|
||||
|
||||
# 格式转换配置
|
||||
format_acceptance_config: Optional[Dict[str, Any]] = Field(
|
||||
format_acceptance_config: dict[str, Any] | None = Field(
|
||||
default=None,
|
||||
description="格式接受策略配置(跨格式转换开关/白黑名单等)",
|
||||
)
|
||||
@@ -75,31 +75,31 @@ class ProviderEndpointCreate(BaseModel):
|
||||
class ProviderEndpointUpdate(BaseModel):
|
||||
"""更新 Endpoint 请求"""
|
||||
|
||||
base_url: Optional[str] = Field(
|
||||
base_url: str | None = Field(
|
||||
default=None, min_length=1, max_length=500, description="API 基础 URL"
|
||||
)
|
||||
custom_path: Optional[str] = Field(default=None, max_length=200, description="自定义请求路径")
|
||||
custom_path: str | None = Field(default=None, max_length=200, description="自定义请求路径")
|
||||
|
||||
# 请求头配置
|
||||
header_rules: Optional[List[HeaderRule]] = Field(
|
||||
header_rules: list[HeaderRule] | None = Field(
|
||||
default=None,
|
||||
description="请求头规则列表,支持 set/drop/rename 操作",
|
||||
)
|
||||
|
||||
max_retries: Optional[int] = Field(default=None, ge=0, le=10, description="最大重试次数")
|
||||
is_active: Optional[bool] = Field(default=None, description="是否启用")
|
||||
config: Optional[Dict[str, Any]] = Field(default=None, description="额外配置")
|
||||
proxy: Optional[ProxyConfig] = Field(default=None, description="代理配置")
|
||||
max_retries: int | None = Field(default=None, ge=0, le=10, description="最大重试次数")
|
||||
is_active: bool | None = Field(default=None, description="是否启用")
|
||||
config: dict[str, Any] | None = Field(default=None, description="额外配置")
|
||||
proxy: ProxyConfig | None = Field(default=None, description="代理配置")
|
||||
|
||||
# 格式转换配置
|
||||
format_acceptance_config: Optional[Dict[str, Any]] = Field(
|
||||
format_acceptance_config: dict[str, Any] | None = Field(
|
||||
default=None,
|
||||
description="格式接受策略配置(跨格式转换开关/白黑名单等)",
|
||||
)
|
||||
|
||||
@field_validator("base_url")
|
||||
@classmethod
|
||||
def validate_base_url(cls, v: Optional[str]) -> Optional[str]:
|
||||
def validate_base_url(cls, v: str | None) -> str | None:
|
||||
"""验证 API URL"""
|
||||
if v is None:
|
||||
return v
|
||||
@@ -120,10 +120,10 @@ class ProviderEndpointResponse(BaseModel):
|
||||
# API 配置
|
||||
api_format: str
|
||||
base_url: str
|
||||
custom_path: Optional[str] = None
|
||||
custom_path: str | None = None
|
||||
|
||||
# 请求头配置
|
||||
header_rules: Optional[List[HeaderRule]] = Field(
|
||||
header_rules: list[HeaderRule] | None = Field(
|
||||
default=None, description="请求头规则列表"
|
||||
)
|
||||
|
||||
@@ -133,13 +133,13 @@ class ProviderEndpointResponse(BaseModel):
|
||||
is_active: bool
|
||||
|
||||
# 额外配置
|
||||
config: Optional[Dict[str, Any]] = None
|
||||
config: dict[str, Any] | None = None
|
||||
|
||||
# 代理配置(响应中密码已脱敏)
|
||||
proxy: Optional[Dict[str, Any]] = Field(default=None, description="代理配置(密码已脱敏)")
|
||||
proxy: dict[str, Any] | None = Field(default=None, description="代理配置(密码已脱敏)")
|
||||
|
||||
# 格式转换配置
|
||||
format_acceptance_config: Optional[Dict[str, Any]] = Field(
|
||||
format_acceptance_config: dict[str, Any] | None = Field(
|
||||
default=None,
|
||||
description="格式接受策略配置(跨格式转换开关/白黑名单等)",
|
||||
)
|
||||
@@ -161,8 +161,8 @@ class ProviderEndpointResponse(BaseModel):
|
||||
class EndpointAPIKeyCreate(BaseModel):
|
||||
"""为 Provider 添加 API Key"""
|
||||
|
||||
provider_id: Optional[str] = Field(default=None, description="Provider ID(从 URL 获取)")
|
||||
api_formats: Optional[List[str]] = Field(
|
||||
provider_id: str | None = Field(default=None, description="Provider ID(从 URL 获取)")
|
||||
api_formats: list[str] | None = Field(
|
||||
default=None, min_length=1, description="支持的 API 格式列表(必填,路由层校验)"
|
||||
)
|
||||
|
||||
@@ -170,23 +170,23 @@ class EndpointAPIKeyCreate(BaseModel):
|
||||
name: str = Field(..., min_length=1, max_length=100, description="密钥名称(必填,用于识别)")
|
||||
|
||||
# 成本计算
|
||||
rate_multipliers: Optional[Dict[str, float]] = Field(
|
||||
rate_multipliers: dict[str, float] | None = Field(
|
||||
default=None, description="按 API 格式的成本倍率,如 {'CLAUDE_CLI': 1.0, 'OPENAI_CLI': 0.8}"
|
||||
)
|
||||
|
||||
# 优先级和限制(数字越小越优先)
|
||||
internal_priority: int = Field(default=50, description="Key 内部优先级(提供商优先模式)")
|
||||
# rpm_limit: NULL=自适应模式(系统自动学习),数字=固定限制模式
|
||||
rpm_limit: Optional[int] = Field(
|
||||
rpm_limit: int | None = Field(
|
||||
default=None, ge=1, le=10000, description="RPM 限制(NULL=自适应模式)"
|
||||
)
|
||||
allowed_models: Optional[List[str]] = Field(
|
||||
allowed_models: list[str] | None = Field(
|
||||
default=None,
|
||||
description="允许使用的模型列表(null=不限制)",
|
||||
)
|
||||
|
||||
# 能力标签
|
||||
capabilities: Optional[Dict[str, bool]] = Field(
|
||||
capabilities: dict[str, bool] | None = Field(
|
||||
default=None, description="Key 能力标签,如 {'cache_1h': true, 'context_1m': true}"
|
||||
)
|
||||
|
||||
@@ -199,7 +199,7 @@ class EndpointAPIKeyCreate(BaseModel):
|
||||
)
|
||||
|
||||
# 备注
|
||||
note: Optional[str] = Field(default=None, max_length=500, description="备注说明(可选)")
|
||||
note: str | None = Field(default=None, max_length=500, description="备注说明(可选)")
|
||||
|
||||
# 自动获取模型
|
||||
auto_fetch_models: bool = Field(
|
||||
@@ -207,21 +207,21 @@ class EndpointAPIKeyCreate(BaseModel):
|
||||
)
|
||||
|
||||
# 锁定的模型列表
|
||||
locked_models: Optional[List[str]] = Field(
|
||||
locked_models: list[str] | None = Field(
|
||||
default=None, description="被锁定的模型列表(刷新时不会被删除)"
|
||||
)
|
||||
|
||||
# 模型过滤规则(仅当 auto_fetch_models=True 时生效)
|
||||
model_include_patterns: Optional[List[str]] = Field(
|
||||
model_include_patterns: list[str] | None = Field(
|
||||
default=None, description="模型包含规则(支持 * 和 ? 通配符),空表示包含所有"
|
||||
)
|
||||
model_exclude_patterns: Optional[List[str]] = Field(
|
||||
model_exclude_patterns: list[str] | None = Field(
|
||||
default=None, description="模型排除规则(支持 * 和 ? 通配符),空表示不排除"
|
||||
)
|
||||
|
||||
@field_validator("api_formats")
|
||||
@classmethod
|
||||
def validate_api_formats(cls, v: Optional[List[str]]) -> Optional[List[str]]:
|
||||
def validate_api_formats(cls, v: list[str] | None) -> list[str] | None:
|
||||
"""验证 API 格式列表"""
|
||||
if v is None:
|
||||
return v
|
||||
@@ -243,7 +243,7 @@ class EndpointAPIKeyCreate(BaseModel):
|
||||
|
||||
@field_validator("allowed_models")
|
||||
@classmethod
|
||||
def validate_allowed_models(cls, v: Optional[List[str]]) -> Optional[List[str]]:
|
||||
def validate_allowed_models(cls, v: list[str] | None) -> list[str] | None:
|
||||
"""
|
||||
规范化 allowed_models:去空、去重、保留顺序
|
||||
"""
|
||||
@@ -253,7 +253,7 @@ class EndpointAPIKeyCreate(BaseModel):
|
||||
if not isinstance(v, list):
|
||||
raise ValueError("allowed_models 必须是列表")
|
||||
|
||||
cleaned: List[str] = []
|
||||
cleaned: list[str] = []
|
||||
seen: set[str] = set()
|
||||
for item in v:
|
||||
if not isinstance(item, str):
|
||||
@@ -293,7 +293,7 @@ class EndpointAPIKeyCreate(BaseModel):
|
||||
|
||||
@field_validator("note")
|
||||
@classmethod
|
||||
def validate_note(cls, v: Optional[str]) -> Optional[str]:
|
||||
def validate_note(cls, v: str | None) -> str | None:
|
||||
"""验证备注(防止 XSS)"""
|
||||
if v is None:
|
||||
return v
|
||||
@@ -308,62 +308,62 @@ class EndpointAPIKeyCreate(BaseModel):
|
||||
class EndpointAPIKeyUpdate(BaseModel):
|
||||
"""更新 Endpoint API Key"""
|
||||
|
||||
api_formats: Optional[List[str]] = Field(
|
||||
api_formats: list[str] | None = Field(
|
||||
default=None, min_length=1, description="支持的 API 格式列表"
|
||||
)
|
||||
|
||||
api_key: Optional[str] = Field(
|
||||
api_key: str | None = Field(
|
||||
default=None, min_length=3, max_length=500, description="API Key(将自动加密)"
|
||||
)
|
||||
name: Optional[str] = Field(default=None, min_length=1, max_length=100, description="密钥名称")
|
||||
rate_multipliers: Optional[Dict[str, float]] = Field(
|
||||
name: str | None = Field(default=None, min_length=1, max_length=100, description="密钥名称")
|
||||
rate_multipliers: dict[str, float] | None = Field(
|
||||
default=None, description="按 API 格式的成本倍率,如 {'CLAUDE_CLI': 1.0, 'OPENAI_CLI': 0.8}"
|
||||
)
|
||||
internal_priority: Optional[int] = Field(
|
||||
internal_priority: int | None = Field(
|
||||
default=None, description="Key 内部优先级(提供商优先模式,数字越小越优先)"
|
||||
)
|
||||
global_priority_by_format: Optional[Dict[str, int]] = Field(
|
||||
global_priority_by_format: dict[str, int] | None = Field(
|
||||
default=None, description="按 API 格式的全局优先级,如 {'CLAUDE': 1, 'CLAUDE_CLI': 2}"
|
||||
)
|
||||
# rpm_limit: 使用特殊标记区分"未提供"和"设置为 null(自适应模式)"
|
||||
# - 不提供字段:不更新
|
||||
# - 提供 null:切换为自适应模式
|
||||
# - 提供数字:设置固定 RPM 限制
|
||||
rpm_limit: Optional[int] = Field(
|
||||
rpm_limit: int | None = Field(
|
||||
default=None, ge=1, le=10000, description="RPM 限制(null=自适应模式)"
|
||||
)
|
||||
allowed_models: Optional[List[str]] = Field(
|
||||
allowed_models: list[str] | None = Field(
|
||||
default=None,
|
||||
description="允许使用的模型列表(null=不限制)",
|
||||
)
|
||||
capabilities: Optional[Dict[str, bool]] = Field(
|
||||
capabilities: dict[str, bool] | None = Field(
|
||||
default=None, description="Key 能力标签,如 {'cache_1h': true, 'context_1m': true}"
|
||||
)
|
||||
cache_ttl_minutes: Optional[int] = Field(
|
||||
cache_ttl_minutes: int | None = Field(
|
||||
default=None, ge=0, le=60, description="缓存 TTL(分钟),0=禁用"
|
||||
)
|
||||
max_probe_interval_minutes: Optional[int] = Field(
|
||||
max_probe_interval_minutes: int | None = Field(
|
||||
default=None, ge=2, le=32, description="熔断探测间隔(分钟),范围 2-32"
|
||||
)
|
||||
is_active: Optional[bool] = Field(default=None, description="是否启用")
|
||||
note: Optional[str] = Field(default=None, max_length=500, description="备注说明")
|
||||
auto_fetch_models: Optional[bool] = Field(
|
||||
is_active: bool | None = Field(default=None, description="是否启用")
|
||||
note: str | None = Field(default=None, max_length=500, description="备注说明")
|
||||
auto_fetch_models: bool | None = Field(
|
||||
default=None, description="是否启用自动获取模型"
|
||||
)
|
||||
locked_models: Optional[List[str]] = Field(
|
||||
locked_models: list[str] | None = Field(
|
||||
default=None, description="被锁定的模型列表(刷新时不会被删除)"
|
||||
)
|
||||
# 模型过滤规则(仅当 auto_fetch_models=True 时生效)
|
||||
model_include_patterns: Optional[List[str]] = Field(
|
||||
model_include_patterns: list[str] | None = Field(
|
||||
default=None, description="模型包含规则(支持 * 和 ? 通配符),空表示包含所有"
|
||||
)
|
||||
model_exclude_patterns: Optional[List[str]] = Field(
|
||||
model_exclude_patterns: list[str] | None = Field(
|
||||
default=None, description="模型排除规则(支持 * 和 ? 通配符),空表示不排除"
|
||||
)
|
||||
|
||||
@field_validator("api_formats")
|
||||
@classmethod
|
||||
def validate_api_formats(cls, v: Optional[List[str]]) -> Optional[List[str]]:
|
||||
def validate_api_formats(cls, v: list[str] | None) -> list[str] | None:
|
||||
"""验证 API 格式列表"""
|
||||
if v is None:
|
||||
return v
|
||||
@@ -385,13 +385,13 @@ class EndpointAPIKeyUpdate(BaseModel):
|
||||
|
||||
@field_validator("allowed_models")
|
||||
@classmethod
|
||||
def validate_allowed_models(cls, v: Optional[List[str]]) -> Optional[List[str]]:
|
||||
def validate_allowed_models(cls, v: list[str] | None) -> list[str] | None:
|
||||
# 与 EndpointAPIKeyCreate 保持一致
|
||||
return EndpointAPIKeyCreate.validate_allowed_models(v)
|
||||
|
||||
@field_validator("api_key")
|
||||
@classmethod
|
||||
def validate_api_key(cls, v: Optional[str]) -> Optional[str]:
|
||||
def validate_api_key(cls, v: str | None) -> str | None:
|
||||
"""验证 API Key 安全性"""
|
||||
if v is None:
|
||||
return v
|
||||
@@ -409,7 +409,7 @@ class EndpointAPIKeyUpdate(BaseModel):
|
||||
|
||||
@field_validator("name")
|
||||
@classmethod
|
||||
def validate_name(cls, v: Optional[str]) -> Optional[str]:
|
||||
def validate_name(cls, v: str | None) -> str | None:
|
||||
"""验证名称(防止 XSS)"""
|
||||
if v is None:
|
||||
return v
|
||||
@@ -422,7 +422,7 @@ class EndpointAPIKeyUpdate(BaseModel):
|
||||
|
||||
@field_validator("note")
|
||||
@classmethod
|
||||
def validate_note(cls, v: Optional[str]) -> Optional[str]:
|
||||
def validate_note(cls, v: str | None) -> str | None:
|
||||
"""验证备注(防止 XSS)"""
|
||||
if v is None:
|
||||
return v
|
||||
@@ -440,52 +440,52 @@ class EndpointAPIKeyResponse(BaseModel):
|
||||
id: str
|
||||
|
||||
provider_id: str = Field(..., description="Provider ID")
|
||||
api_formats: List[str] = Field(default=[], description="支持的 API 格式列表")
|
||||
api_formats: list[str] = Field(default=[], description="支持的 API 格式列表")
|
||||
|
||||
# Key 信息(脱敏)
|
||||
api_key_masked: str = Field(..., description="脱敏后的 Key")
|
||||
api_key_plain: Optional[str] = Field(default=None, description="完整的 Key")
|
||||
api_key_plain: str | None = Field(default=None, description="完整的 Key")
|
||||
name: str = Field(..., description="密钥名称")
|
||||
|
||||
# 成本计算
|
||||
rate_multipliers: Optional[Dict[str, float]] = Field(
|
||||
rate_multipliers: dict[str, float] | None = Field(
|
||||
default=None, description="按 API 格式的成本倍率,如 {'CLAUDE_CLI': 1.0, 'OPENAI_CLI': 0.8}"
|
||||
)
|
||||
|
||||
# 优先级和限制
|
||||
internal_priority: int = Field(default=50, description="Endpoint 内部优先级")
|
||||
global_priority_by_format: Optional[Dict[str, int]] = Field(
|
||||
global_priority_by_format: dict[str, int] | None = Field(
|
||||
default=None, description="按 API 格式的全局优先级"
|
||||
)
|
||||
rpm_limit: Optional[int] = None
|
||||
allowed_models: Optional[List[str]] = None
|
||||
capabilities: Optional[Dict[str, bool]] = Field(default=None, description="Key 能力标签")
|
||||
rpm_limit: int | None = None
|
||||
allowed_models: list[str] | None = None
|
||||
capabilities: dict[str, bool] | None = Field(default=None, description="Key 能力标签")
|
||||
|
||||
# 缓存与熔断配置
|
||||
cache_ttl_minutes: int = Field(default=5, description="缓存 TTL(分钟),0=禁用")
|
||||
max_probe_interval_minutes: int = Field(default=32, description="熔断探测间隔(分钟)")
|
||||
|
||||
# 按格式的健康度数据
|
||||
health_by_format: Optional[Dict[str, Any]] = Field(
|
||||
health_by_format: dict[str, Any] | None = Field(
|
||||
default=None, description="按 API 格式存储的健康度数据"
|
||||
)
|
||||
circuit_breaker_by_format: Optional[Dict[str, Any]] = Field(
|
||||
circuit_breaker_by_format: dict[str, Any] | None = Field(
|
||||
default=None, description="按 API 格式存储的熔断器状态"
|
||||
)
|
||||
|
||||
# 聚合字段(从 health_by_format 计算,用于列表显示)
|
||||
health_score: float = Field(default=1.0, description="健康度(所有格式中的最低值)")
|
||||
consecutive_failures: int = Field(default=0, description="连续失败次数")
|
||||
last_failure_at: Optional[datetime] = None
|
||||
last_failure_at: datetime | None = None
|
||||
|
||||
# 聚合熔断器字段
|
||||
circuit_breaker_open: bool = Field(default=False, description="熔断器是否打开(任何格式)")
|
||||
circuit_breaker_open_at: Optional[datetime] = Field(default=None, description="熔断器打开时间")
|
||||
next_probe_at: Optional[datetime] = Field(default=None, description="下次进入半开状态时间")
|
||||
half_open_until: Optional[datetime] = Field(default=None, description="半开状态结束时间")
|
||||
half_open_successes: Optional[int] = Field(default=0, description="半开状态成功次数")
|
||||
half_open_failures: Optional[int] = Field(default=0, description="半开状态失败次数")
|
||||
request_results_window: Optional[List[dict]] = Field(None, description="请求结果滑动窗口")
|
||||
circuit_breaker_open_at: datetime | None = Field(default=None, description="熔断器打开时间")
|
||||
next_probe_at: datetime | None = Field(default=None, description="下次进入半开状态时间")
|
||||
half_open_until: datetime | None = Field(default=None, description="半开状态结束时间")
|
||||
half_open_successes: int | None = Field(default=0, description="半开状态成功次数")
|
||||
half_open_failures: int | None = Field(default=0, description="半开状态失败次数")
|
||||
request_results_window: list[dict] | None = Field(None, description="请求结果滑动窗口")
|
||||
|
||||
# 使用统计
|
||||
request_count: int
|
||||
@@ -499,30 +499,30 @@ class EndpointAPIKeyResponse(BaseModel):
|
||||
|
||||
# 自适应 RPM 信息
|
||||
is_adaptive: bool = Field(default=False, description="是否为自适应模式(rpm_limit=NULL)")
|
||||
learned_rpm_limit: Optional[int] = Field(None, description="学习到的 RPM 限制")
|
||||
effective_limit: Optional[int] = Field(None, description="当前有效限制")
|
||||
learned_rpm_limit: int | None = Field(None, description="学习到的 RPM 限制")
|
||||
effective_limit: int | None = Field(None, description="当前有效限制")
|
||||
# 滑动窗口利用率采样
|
||||
utilization_samples: Optional[List[dict]] = Field(None, description="利用率采样窗口")
|
||||
last_probe_increase_at: Optional[datetime] = Field(None, description="上次探测性扩容时间")
|
||||
concurrent_429_count: Optional[int] = None
|
||||
rpm_429_count: Optional[int] = None
|
||||
last_429_at: Optional[datetime] = None
|
||||
last_429_type: Optional[str] = None
|
||||
utilization_samples: list[dict] | None = Field(None, description="利用率采样窗口")
|
||||
last_probe_increase_at: datetime | None = Field(None, description="上次探测性扩容时间")
|
||||
concurrent_429_count: int | None = None
|
||||
rpm_429_count: int | None = None
|
||||
last_429_at: datetime | None = None
|
||||
last_429_type: str | None = None
|
||||
|
||||
# 备注
|
||||
note: Optional[str] = None
|
||||
note: str | None = None
|
||||
|
||||
# 自动获取模型
|
||||
auto_fetch_models: bool = Field(default=False, description="是否启用自动获取模型")
|
||||
last_models_fetch_at: Optional[datetime] = Field(None, description="最后获取模型时间")
|
||||
last_models_fetch_error: Optional[str] = Field(None, description="最后获取模型错误信息")
|
||||
locked_models: Optional[List[str]] = Field(None, description="被锁定的模型列表")
|
||||
last_models_fetch_at: datetime | None = Field(None, description="最后获取模型时间")
|
||||
last_models_fetch_error: str | None = Field(None, description="最后获取模型错误信息")
|
||||
locked_models: list[str] | None = Field(None, description="被锁定的模型列表")
|
||||
# 模型过滤规则
|
||||
model_include_patterns: Optional[List[str]] = Field(None, description="模型包含规则")
|
||||
model_exclude_patterns: Optional[List[str]] = Field(None, description="模型排除规则")
|
||||
model_include_patterns: list[str] | None = Field(None, description="模型包含规则")
|
||||
model_exclude_patterns: list[str] | None = Field(None, description="模型排除规则")
|
||||
|
||||
# 时间戳
|
||||
last_used_at: Optional[datetime] = None
|
||||
last_used_at: datetime | None = None
|
||||
created_at: datetime
|
||||
updated_at: datetime
|
||||
|
||||
@@ -539,8 +539,8 @@ class FormatHealthData(BaseModel):
|
||||
error_rate: float = 0.0
|
||||
window_size: int = 0
|
||||
consecutive_failures: int = 0
|
||||
last_failure_at: Optional[str] = None
|
||||
circuit_breaker: Dict[str, Any] = Field(default_factory=dict)
|
||||
last_failure_at: str | None = None
|
||||
circuit_breaker: dict[str, Any] = Field(default_factory=dict)
|
||||
|
||||
|
||||
class HealthStatusResponse(BaseModel):
|
||||
@@ -549,25 +549,25 @@ class HealthStatusResponse(BaseModel):
|
||||
# 基础信息
|
||||
key_id: str
|
||||
key_is_active: bool
|
||||
key_statistics: Optional[Dict[str, Any]] = None
|
||||
key_statistics: dict[str, Any] | None = None
|
||||
|
||||
# 整体健康度(取所有格式中的最低值)
|
||||
key_health_score: float = 1.0
|
||||
any_circuit_open: bool = False
|
||||
|
||||
# 按格式的健康度数据
|
||||
health_by_format: Optional[Dict[str, FormatHealthData]] = None
|
||||
health_by_format: dict[str, FormatHealthData] | None = None
|
||||
|
||||
# 单格式查询时的字段
|
||||
api_format: Optional[str] = None
|
||||
key_consecutive_failures: Optional[int] = None
|
||||
key_last_failure_at: Optional[str] = None
|
||||
api_format: str | None = None
|
||||
key_consecutive_failures: int | None = None
|
||||
key_last_failure_at: str | None = None
|
||||
|
||||
# 单格式查询时的熔断器状态
|
||||
circuit_breaker_open: bool = False
|
||||
circuit_breaker_open_at: Optional[str] = None
|
||||
next_probe_at: Optional[str] = None
|
||||
half_open_until: Optional[str] = None
|
||||
circuit_breaker_open_at: str | None = None
|
||||
next_probe_at: str | None = None
|
||||
half_open_until: str | None = None
|
||||
half_open_successes: int = 0
|
||||
half_open_failures: int = 0
|
||||
|
||||
@@ -575,8 +575,8 @@ class HealthStatusResponse(BaseModel):
|
||||
class HealthSummaryResponse(BaseModel):
|
||||
"""健康状态摘要"""
|
||||
|
||||
endpoints: Dict[str, int] = Field(..., description="Endpoint 统计 (total, active, unhealthy)")
|
||||
keys: Dict[str, int] = Field(..., description="Key 统计 (total, active, unhealthy)")
|
||||
endpoints: dict[str, int] = Field(..., description="Endpoint 统计 (total, active, unhealthy)")
|
||||
keys: dict[str, int] = Field(..., description="Key 统计 (total, active, unhealthy)")
|
||||
|
||||
|
||||
# ========== RPM 控制相关 ==========
|
||||
@@ -587,7 +587,7 @@ class KeyRpmStatusResponse(BaseModel):
|
||||
|
||||
key_id: str = Field(..., description="Key ID")
|
||||
current_rpm: int = Field(default=0, description="当前 RPM 计数")
|
||||
rpm_limit: Optional[int] = Field(default=None, description="RPM 限制")
|
||||
rpm_limit: int | None = Field(default=None, description="RPM 限制")
|
||||
|
||||
|
||||
class KeyPriorityItem(BaseModel):
|
||||
@@ -600,7 +600,7 @@ class KeyPriorityItem(BaseModel):
|
||||
class BatchUpdateKeyPriorityRequest(BaseModel):
|
||||
"""批量更新 Key 优先级请求"""
|
||||
|
||||
priorities: List[KeyPriorityItem] = Field(..., min_length=1, description="Key 优先级列表")
|
||||
priorities: list[KeyPriorityItem] = Field(..., min_length=1, description="Key 优先级列表")
|
||||
|
||||
|
||||
# ========== 提供商摘要(增强版) ==========
|
||||
@@ -609,27 +609,27 @@ class BatchUpdateKeyPriorityRequest(BaseModel):
|
||||
class ProviderUpdateRequest(BaseModel):
|
||||
"""Provider 基础配置更新请求"""
|
||||
|
||||
name: Optional[str] = Field(None, min_length=1, max_length=100)
|
||||
description: Optional[str] = None
|
||||
website: Optional[str] = Field(None, max_length=500, description="主站网站")
|
||||
provider_priority: Optional[int] = Field(None, description="提供商优先级(数字越小越优先)")
|
||||
keep_priority_on_conversion: Optional[bool] = Field(
|
||||
name: str | None = Field(None, min_length=1, max_length=100)
|
||||
description: str | None = None
|
||||
website: str | None = Field(None, max_length=500, description="主站网站")
|
||||
provider_priority: int | None = Field(None, description="提供商优先级(数字越小越优先)")
|
||||
keep_priority_on_conversion: bool | None = Field(
|
||||
None,
|
||||
description="格式转换时是否保持优先级(True=保持原优先级,False=需要转换时降级)",
|
||||
)
|
||||
is_active: Optional[bool] = None
|
||||
billing_type: Optional[str] = Field(
|
||||
is_active: bool | None = None
|
||||
billing_type: str | None = Field(
|
||||
None, description="计费类型:monthly_quota/pay_as_you_go/free_tier"
|
||||
)
|
||||
monthly_quota_usd: Optional[float] = Field(None, ge=0, description="订阅配额(美元)")
|
||||
quota_reset_day: Optional[int] = Field(None, ge=1, le=31, description="配额重置日(1-31)")
|
||||
quota_expires_at: Optional[datetime] = Field(None, description="配额过期时间")
|
||||
monthly_quota_usd: float | None = Field(None, ge=0, description="订阅配额(美元)")
|
||||
quota_reset_day: int | None = Field(None, ge=1, le=31, description="配额重置日(1-31)")
|
||||
quota_expires_at: datetime | None = Field(None, description="配额过期时间")
|
||||
# 请求配置(从 Endpoint 迁移)
|
||||
max_retries: Optional[int] = Field(None, ge=0, le=10, description="最大重试次数")
|
||||
proxy: Optional[Dict[str, Any]] = Field(None, description="代理配置")
|
||||
max_retries: int | None = Field(None, ge=0, le=10, description="最大重试次数")
|
||||
proxy: dict[str, Any] | None = Field(None, description="代理配置")
|
||||
# 超时配置(秒),为空时使用全局配置
|
||||
stream_first_byte_timeout: Optional[float] = Field(None, ge=1, le=300, description="流式请求首字节超时(秒)")
|
||||
request_timeout: Optional[float] = Field(None, ge=1, le=600, description="非流式请求整体超时(秒)")
|
||||
stream_first_byte_timeout: float | None = Field(None, ge=1, le=300, description="流式请求首字节超时(秒)")
|
||||
request_timeout: float | None = Field(None, ge=1, le=600, description="非流式请求整体超时(秒)")
|
||||
|
||||
|
||||
class ProviderWithEndpointsSummary(BaseModel):
|
||||
@@ -638,8 +638,8 @@ class ProviderWithEndpointsSummary(BaseModel):
|
||||
# Provider 基本信息
|
||||
id: str
|
||||
name: str
|
||||
description: Optional[str] = None
|
||||
website: Optional[str] = None
|
||||
description: str | None = None
|
||||
website: str | None = None
|
||||
provider_priority: int = Field(default=100, description="提供商优先级(数字越小越优先)")
|
||||
keep_priority_on_conversion: bool = Field(
|
||||
default=False,
|
||||
@@ -648,19 +648,19 @@ class ProviderWithEndpointsSummary(BaseModel):
|
||||
is_active: bool
|
||||
|
||||
# 计费相关字段
|
||||
billing_type: Optional[str] = None
|
||||
monthly_quota_usd: Optional[float] = None
|
||||
monthly_used_usd: Optional[float] = None
|
||||
quota_reset_day: Optional[int] = Field(default=None, description="配额重置周期(天数)")
|
||||
quota_last_reset_at: Optional[datetime] = Field(default=None, description="当前周期开始时间")
|
||||
quota_expires_at: Optional[datetime] = Field(default=None, description="配额过期时间")
|
||||
billing_type: str | None = None
|
||||
monthly_quota_usd: float | None = None
|
||||
monthly_used_usd: float | None = None
|
||||
quota_reset_day: int | None = Field(default=None, description="配额重置周期(天数)")
|
||||
quota_last_reset_at: datetime | None = Field(default=None, description="当前周期开始时间")
|
||||
quota_expires_at: datetime | None = Field(default=None, description="配额过期时间")
|
||||
|
||||
# 请求配置(从 Endpoint 迁移)
|
||||
max_retries: Optional[int] = Field(default=2, description="最大重试次数")
|
||||
proxy: Optional[Dict[str, Any]] = Field(default=None, description="代理配置")
|
||||
max_retries: int | None = Field(default=2, description="最大重试次数")
|
||||
proxy: dict[str, Any] | None = Field(default=None, description="代理配置")
|
||||
# 超时配置(秒),为空时使用全局配置
|
||||
stream_first_byte_timeout: Optional[float] = Field(default=None, description="流式请求首字节超时(秒)")
|
||||
request_timeout: Optional[float] = Field(default=None, description="非流式请求整体超时(秒)")
|
||||
stream_first_byte_timeout: float | None = Field(default=None, description="流式请求首字节超时(秒)")
|
||||
request_timeout: float | None = Field(default=None, description="非流式请求整体超时(秒)")
|
||||
|
||||
# Endpoint 统计
|
||||
total_endpoints: int = Field(default=0, description="总 Endpoint 数量")
|
||||
@@ -675,10 +675,10 @@ class ProviderWithEndpointsSummary(BaseModel):
|
||||
active_models: int = Field(default=0, description="活跃模型数量")
|
||||
|
||||
# API 格式列表
|
||||
api_formats: List[str] = Field(default=[], description="支持的 API 格式列表")
|
||||
api_formats: list[str] = Field(default=[], description="支持的 API 格式列表")
|
||||
|
||||
# Endpoint 健康度详情
|
||||
endpoint_health_details: List[Dict[str, Any]] = Field(
|
||||
endpoint_health_details: list[dict[str, Any]] = Field(
|
||||
default=[],
|
||||
description="每个 Endpoint 的健康度详情 [{api_format: str, health_score: float, is_active: bool}]",
|
||||
)
|
||||
@@ -691,7 +691,7 @@ class ProviderWithEndpointsSummary(BaseModel):
|
||||
|
||||
# Provider Ops 配置状态
|
||||
ops_configured: bool = Field(default=False, description="是否配置了扩展操作(余额监控等)")
|
||||
ops_architecture_id: Optional[str] = Field(
|
||||
ops_architecture_id: str | None = Field(
|
||||
default=None, description="扩展操作使用的架构 ID(如 cubence, anyrouter)"
|
||||
)
|
||||
|
||||
@@ -710,10 +710,10 @@ class EndpointHealthEvent(BaseModel):
|
||||
|
||||
timestamp: datetime
|
||||
status: str
|
||||
status_code: Optional[int] = None
|
||||
latency_ms: Optional[int] = None
|
||||
error_type: Optional[str] = None
|
||||
error_message: Optional[str] = None
|
||||
status_code: int | None = None
|
||||
latency_ms: int | None = None
|
||||
error_type: str | None = None
|
||||
error_message: str | None = None
|
||||
|
||||
|
||||
class EndpointHealthMonitor(BaseModel):
|
||||
@@ -727,8 +727,8 @@ class EndpointHealthMonitor(BaseModel):
|
||||
failed_count: int
|
||||
skipped_count: int
|
||||
success_rate: float = Field(default=1.0, description="最近事件窗口的成功率")
|
||||
last_event_at: Optional[datetime] = None
|
||||
events: List[EndpointHealthEvent] = Field(default_factory=list)
|
||||
last_event_at: datetime | None = None
|
||||
events: list[EndpointHealthEvent] = Field(default_factory=list)
|
||||
|
||||
|
||||
class ProviderEndpointHealthMonitorResponse(BaseModel):
|
||||
@@ -737,7 +737,7 @@ class ProviderEndpointHealthMonitorResponse(BaseModel):
|
||||
provider_id: str
|
||||
provider_name: str
|
||||
generated_at: datetime
|
||||
endpoints: List[EndpointHealthMonitor] = Field(default_factory=list)
|
||||
endpoints: list[EndpointHealthMonitor] = Field(default_factory=list)
|
||||
|
||||
|
||||
class ApiFormatHealthMonitor(BaseModel):
|
||||
@@ -751,16 +751,16 @@ class ApiFormatHealthMonitor(BaseModel):
|
||||
success_rate: float = Field(default=1.0, description="最近事件窗口的成功率")
|
||||
provider_count: int = Field(default=0, description="参与统计的 Provider 数量")
|
||||
key_count: int = Field(default=0, description="参与统计的 API Key 数量")
|
||||
last_event_at: Optional[datetime] = None
|
||||
events: List[EndpointHealthEvent] = Field(default_factory=list)
|
||||
timeline: List[str] = Field(
|
||||
last_event_at: datetime | None = None
|
||||
events: list[EndpointHealthEvent] = Field(default_factory=list)
|
||||
timeline: list[str] = Field(
|
||||
default_factory=list,
|
||||
description="Usage 表生成的健康时间线(healthy/warning/unhealthy/unknown)",
|
||||
)
|
||||
time_range_start: Optional[datetime] = Field(
|
||||
time_range_start: datetime | None = Field(
|
||||
default=None, description="时间线所覆盖区间的开始时间"
|
||||
)
|
||||
time_range_end: Optional[datetime] = Field(
|
||||
time_range_end: datetime | None = Field(
|
||||
default=None, description="时间线所覆盖区间的结束时间"
|
||||
)
|
||||
|
||||
@@ -769,7 +769,7 @@ class ApiFormatHealthMonitorResponse(BaseModel):
|
||||
"""所有 API 格式的健康监控汇总"""
|
||||
|
||||
generated_at: datetime
|
||||
formats: List[ApiFormatHealthMonitor] = Field(default_factory=list)
|
||||
formats: list[ApiFormatHealthMonitor] = Field(default_factory=list)
|
||||
|
||||
|
||||
# ========== 公开健康监控模型(不含敏感信息) ==========
|
||||
@@ -780,9 +780,9 @@ class PublicHealthEvent(BaseModel):
|
||||
|
||||
timestamp: datetime
|
||||
status: str
|
||||
status_code: Optional[int] = None
|
||||
latency_ms: Optional[int] = None
|
||||
error_type: Optional[str] = None
|
||||
status_code: int | None = None
|
||||
latency_ms: int | None = None
|
||||
error_type: str | None = None
|
||||
|
||||
|
||||
class PublicApiFormatHealthMonitor(BaseModel):
|
||||
@@ -795,18 +795,18 @@ class PublicApiFormatHealthMonitor(BaseModel):
|
||||
failed_count: int = Field(default=0, description="失败次数")
|
||||
skipped_count: int = Field(default=0, description="跳过次数")
|
||||
success_rate: float = Field(default=1.0, description="成功率")
|
||||
last_event_at: Optional[datetime] = None
|
||||
events: List[PublicHealthEvent] = Field(default_factory=list, description="事件列表")
|
||||
timeline: List[str] = Field(
|
||||
last_event_at: datetime | None = None
|
||||
events: list[PublicHealthEvent] = Field(default_factory=list, description="事件列表")
|
||||
timeline: list[str] = Field(
|
||||
default_factory=list,
|
||||
description="Usage 表生成的健康时间线(healthy/warning/unhealthy/unknown)",
|
||||
)
|
||||
time_range_start: Optional[datetime] = Field(default=None, description="时间线覆盖区间开始时间")
|
||||
time_range_end: Optional[datetime] = Field(default=None, description="时间线覆盖区间结束时间")
|
||||
time_range_start: datetime | None = Field(default=None, description="时间线覆盖区间开始时间")
|
||||
time_range_end: datetime | None = Field(default=None, description="时间线覆盖区间结束时间")
|
||||
|
||||
|
||||
class PublicApiFormatHealthMonitorResponse(BaseModel):
|
||||
"""公开版健康监控汇总(不含敏感信息)"""
|
||||
|
||||
generated_at: datetime
|
||||
formats: List[PublicApiFormatHealthMonitor] = Field(default_factory=list)
|
||||
formats: list[PublicApiFormatHealthMonitor] = Field(default_factory=list)
|
||||
|
||||
@@ -5,7 +5,7 @@ Google Gemini API 请求/响应模型
|
||||
作为 API 网关,采用宽松类型定义以支持 API 新特性透传
|
||||
"""
|
||||
|
||||
from typing import Any, Dict, List, Optional
|
||||
from typing import Any
|
||||
|
||||
from pydantic import BaseModel, ConfigDict, Field
|
||||
|
||||
@@ -28,8 +28,8 @@ class GeminiContent(BaseModelWithExtras):
|
||||
使用宽松类型定义,parts 接受任意字典列表以支持 API 新特性
|
||||
"""
|
||||
|
||||
role: Optional[str] = None
|
||||
parts: List[Dict[str, Any]]
|
||||
role: str | None = None
|
||||
parts: list[dict[str, Any]]
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
@@ -51,14 +51,14 @@ class GeminiRequest(BaseModelWithExtras):
|
||||
采用宽松类型定义,除必要字段外全部透传
|
||||
"""
|
||||
|
||||
model: Optional[str] = Field(default=None, description="模型名称,从 URL 路径提取(内部使用)")
|
||||
contents: List[GeminiContent]
|
||||
model: str | None = Field(default=None, description="模型名称,从 URL 路径提取(内部使用)")
|
||||
contents: list[GeminiContent]
|
||||
# 以下字段全部使用 Dict[str, Any] 透传,不做结构验证
|
||||
system_instruction: Optional[Dict[str, Any]] = Field(default=None, alias="systemInstruction")
|
||||
tools: Optional[List[Dict[str, Any]]] = None
|
||||
tool_config: Optional[Dict[str, Any]] = Field(default=None, alias="toolConfig")
|
||||
safety_settings: Optional[List[Dict[str, Any]]] = Field(default=None, alias="safetySettings")
|
||||
generation_config: Optional[Dict[str, Any]] = Field(default=None, alias="generationConfig")
|
||||
system_instruction: dict[str, Any] | None = Field(default=None, alias="systemInstruction")
|
||||
tools: list[dict[str, Any]] | None = None
|
||||
tool_config: dict[str, Any] | None = Field(default=None, alias="toolConfig")
|
||||
safety_settings: list[dict[str, Any]] | None = Field(default=None, alias="safetySettings")
|
||||
generation_config: dict[str, Any] | None = Field(default=None, alias="generationConfig")
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
OpenAI API 数据模型定义
|
||||
"""
|
||||
|
||||
from typing import Any, Dict, List, Optional, Union
|
||||
from typing import Any
|
||||
|
||||
from pydantic import BaseModel, ConfigDict
|
||||
|
||||
@@ -16,18 +16,18 @@ class OpenAIMessage(BaseModelWithExtras):
|
||||
"""OpenAI消息模型"""
|
||||
|
||||
role: str
|
||||
content: Optional[Union[str, List[Dict[str, Any]]]] = None
|
||||
tool_calls: Optional[List[Dict[str, Any]]] = None
|
||||
tool_call_id: Optional[str] = None
|
||||
name: Optional[str] = None
|
||||
content: str | list[dict[str, Any]] | None = None
|
||||
tool_calls: list[dict[str, Any]] | None = None
|
||||
tool_call_id: str | None = None
|
||||
name: str | None = None
|
||||
|
||||
|
||||
class OpenAIFunction(BaseModelWithExtras):
|
||||
"""OpenAI函数定义"""
|
||||
|
||||
name: str
|
||||
description: Optional[str] = None
|
||||
parameters: Dict[str, Any]
|
||||
description: str | None = None
|
||||
parameters: dict[str, Any]
|
||||
|
||||
|
||||
class OpenAITool(BaseModelWithExtras):
|
||||
@@ -41,23 +41,23 @@ class OpenAIRequest(BaseModelWithExtras):
|
||||
"""OpenAI请求模型"""
|
||||
|
||||
model: str
|
||||
messages: List[OpenAIMessage]
|
||||
max_tokens: Optional[int] = None
|
||||
temperature: Optional[float] = 1.0
|
||||
top_p: Optional[float] = None
|
||||
stream: Optional[bool] = False
|
||||
stop: Optional[Union[str, List[str]]] = None
|
||||
tools: Optional[List[OpenAITool]] = None
|
||||
tool_choice: Optional[Union[str, Dict[str, Any]]] = None
|
||||
presence_penalty: Optional[float] = None
|
||||
frequency_penalty: Optional[float] = None
|
||||
n: Optional[int] = None
|
||||
seed: Optional[int] = None
|
||||
response_format: Optional[Dict[str, Any]] = None
|
||||
logit_bias: Optional[Dict[str, float]] = None
|
||||
logprobs: Optional[bool] = None
|
||||
top_logprobs: Optional[int] = None
|
||||
user: Optional[str] = None
|
||||
messages: list[OpenAIMessage]
|
||||
max_tokens: int | None = None
|
||||
temperature: float | None = 1.0
|
||||
top_p: float | None = None
|
||||
stream: bool | None = False
|
||||
stop: str | list[str] | None = None
|
||||
tools: list[OpenAITool] | None = None
|
||||
tool_choice: str | dict[str, Any] | None = None
|
||||
presence_penalty: float | None = None
|
||||
frequency_penalty: float | None = None
|
||||
n: int | None = None
|
||||
seed: int | None = None
|
||||
response_format: dict[str, Any] | None = None
|
||||
logit_bias: dict[str, float] | None = None
|
||||
logprobs: bool | None = None
|
||||
top_logprobs: int | None = None
|
||||
user: str | None = None
|
||||
|
||||
|
||||
class ResponsesInputMessage(BaseModelWithExtras):
|
||||
@@ -65,7 +65,7 @@ class ResponsesInputMessage(BaseModelWithExtras):
|
||||
|
||||
type: str = "message"
|
||||
role: str
|
||||
content: List[Dict[str, Any]]
|
||||
content: list[dict[str, Any]]
|
||||
|
||||
|
||||
class ResponsesReasoningConfig(BaseModelWithExtras):
|
||||
@@ -79,21 +79,21 @@ class ResponsesRequest(BaseModelWithExtras):
|
||||
"""OpenAI Responses API 请求模型(用于 Claude Code 等客户端)"""
|
||||
|
||||
model: str
|
||||
instructions: Optional[str] = None
|
||||
input: List[ResponsesInputMessage]
|
||||
tools: Optional[List[Dict[str, Any]]] = None
|
||||
tool_choice: Optional[Union[str, Dict[str, Any]]] = "auto"
|
||||
parallel_tool_calls: Optional[bool] = False
|
||||
reasoning: Optional[ResponsesReasoningConfig] = None
|
||||
store: Optional[bool] = False
|
||||
stream: Optional[bool] = True
|
||||
include: Optional[List[str]] = None
|
||||
prompt_cache_key: Optional[str] = None
|
||||
instructions: str | None = None
|
||||
input: list[ResponsesInputMessage]
|
||||
tools: list[dict[str, Any]] | None = None
|
||||
tool_choice: str | dict[str, Any] | None = "auto"
|
||||
parallel_tool_calls: bool | None = False
|
||||
reasoning: ResponsesReasoningConfig | None = None
|
||||
store: bool | None = False
|
||||
stream: bool | None = True
|
||||
include: list[str] | None = None
|
||||
prompt_cache_key: str | None = None
|
||||
# 其他参数
|
||||
max_tokens: Optional[int] = None
|
||||
temperature: Optional[float] = None
|
||||
top_p: Optional[float] = None
|
||||
stop: Optional[Union[str, List[str]]] = None
|
||||
max_tokens: int | None = None
|
||||
temperature: float | None = None
|
||||
top_p: float | None = None
|
||||
stop: str | list[str] | None = None
|
||||
|
||||
|
||||
class OpenAIUsage(BaseModelWithExtras):
|
||||
@@ -109,8 +109,8 @@ class OpenAIChoice(BaseModelWithExtras):
|
||||
|
||||
index: int
|
||||
message: OpenAIMessage
|
||||
finish_reason: Optional[str] = None
|
||||
logprobs: Optional[Dict[str, Any]] = None
|
||||
finish_reason: str | None = None
|
||||
logprobs: dict[str, Any] | None = None
|
||||
|
||||
|
||||
class OpenAIResponse(BaseModelWithExtras):
|
||||
@@ -120,17 +120,17 @@ class OpenAIResponse(BaseModelWithExtras):
|
||||
object: str = "chat.completion"
|
||||
created: int
|
||||
model: str
|
||||
choices: List[OpenAIChoice]
|
||||
usage: Optional[OpenAIUsage] = None
|
||||
system_fingerprint: Optional[str] = None
|
||||
choices: list[OpenAIChoice]
|
||||
usage: OpenAIUsage | None = None
|
||||
system_fingerprint: str | None = None
|
||||
|
||||
|
||||
class OpenAIStreamDelta(BaseModelWithExtras):
|
||||
"""OpenAI流式响应增量"""
|
||||
|
||||
role: Optional[str] = None
|
||||
content: Optional[str] = None
|
||||
tool_calls: Optional[List[Dict[str, Any]]] = None
|
||||
role: str | None = None
|
||||
content: str | None = None
|
||||
tool_calls: list[dict[str, Any]] | None = None
|
||||
|
||||
|
||||
class OpenAIStreamChoice(BaseModelWithExtras):
|
||||
@@ -138,8 +138,8 @@ class OpenAIStreamChoice(BaseModelWithExtras):
|
||||
|
||||
index: int
|
||||
delta: OpenAIStreamDelta
|
||||
finish_reason: Optional[str] = None
|
||||
logprobs: Optional[Dict[str, Any]] = None
|
||||
finish_reason: str | None = None
|
||||
logprobs: dict[str, Any] | None = None
|
||||
|
||||
|
||||
class OpenAIStreamResponse(BaseModelWithExtras):
|
||||
@@ -149,5 +149,5 @@ class OpenAIStreamResponse(BaseModelWithExtras):
|
||||
object: str = "chat.completion.chunk"
|
||||
created: int
|
||||
model: str
|
||||
choices: List[OpenAIStreamChoice]
|
||||
system_fingerprint: Optional[str] = None
|
||||
choices: list[OpenAIStreamChoice]
|
||||
system_fingerprint: str | None = None
|
||||
|
||||
@@ -2,8 +2,9 @@
|
||||
Pydantic 数据模型(阶段一统一模型管理)
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
from datetime import datetime
|
||||
from typing import Any, Dict, List, Optional
|
||||
from typing import Any
|
||||
|
||||
from pydantic import BaseModel, ConfigDict, Field, model_validator
|
||||
|
||||
@@ -21,20 +22,20 @@ class CacheTTLPricing(BaseModel):
|
||||
class PricingTier(BaseModel):
|
||||
"""单个价格阶梯配置"""
|
||||
|
||||
up_to: Optional[int] = Field(
|
||||
up_to: int | None = Field(
|
||||
None,
|
||||
ge=1,
|
||||
description="阶梯上限(tokens),null 表示无上限(最后一个阶梯)"
|
||||
)
|
||||
input_price_per_1m: float = Field(..., ge=0, description="输入价格/M tokens")
|
||||
output_price_per_1m: float = Field(..., ge=0, description="输出价格/M tokens")
|
||||
cache_creation_price_per_1m: Optional[float] = Field(
|
||||
cache_creation_price_per_1m: float | None = Field(
|
||||
None, ge=0, description="缓存创建价格/M tokens"
|
||||
)
|
||||
cache_read_price_per_1m: Optional[float] = Field(
|
||||
cache_read_price_per_1m: float | None = Field(
|
||||
None, ge=0, description="缓存读取价格/M tokens"
|
||||
)
|
||||
cache_ttl_pricing: Optional[List[CacheTTLPricing]] = Field(
|
||||
cache_ttl_pricing: list[CacheTTLPricing] | None = Field(
|
||||
None, description="按缓存时长分价格(可选)"
|
||||
)
|
||||
|
||||
@@ -42,14 +43,14 @@ class PricingTier(BaseModel):
|
||||
class TieredPricingConfig(BaseModel):
|
||||
"""阶梯计费配置"""
|
||||
|
||||
tiers: List[PricingTier] = Field(
|
||||
tiers: list[PricingTier] = Field(
|
||||
...,
|
||||
min_length=1,
|
||||
description="价格阶梯列表,按 up_to 升序排列"
|
||||
)
|
||||
|
||||
@model_validator(mode="after")
|
||||
def validate_tiers(self) -> "TieredPricingConfig":
|
||||
def validate_tiers(self) -> TieredPricingConfig:
|
||||
"""验证阶梯配置的合法性"""
|
||||
tiers = self.tiers
|
||||
if not tiers:
|
||||
@@ -103,10 +104,10 @@ class ModelCapabilities(BaseModel):
|
||||
class ModelPriceRange(BaseModel):
|
||||
"""统一模型价格区间"""
|
||||
|
||||
min_input: Optional[float] = None
|
||||
max_input: Optional[float] = None
|
||||
min_output: Optional[float] = None
|
||||
max_output: Optional[float] = None
|
||||
min_input: float | None = None
|
||||
max_input: float | None = None
|
||||
min_output: float | None = None
|
||||
max_output: float | None = None
|
||||
|
||||
|
||||
class ModelCatalogProviderDetail(BaseModel):
|
||||
@@ -114,19 +115,19 @@ class ModelCatalogProviderDetail(BaseModel):
|
||||
|
||||
provider_id: str
|
||||
provider_name: str
|
||||
model_id: Optional[str]
|
||||
model_id: str | None
|
||||
target_model: str
|
||||
input_price_per_1m: Optional[float]
|
||||
output_price_per_1m: Optional[float]
|
||||
cache_creation_price_per_1m: Optional[float]
|
||||
cache_read_price_per_1m: Optional[float]
|
||||
cache_1h_creation_price_per_1m: Optional[float] = None # 1h 缓存创建价格
|
||||
price_per_request: Optional[float] = None # 按次计费价格
|
||||
effective_tiered_pricing: Optional[Dict[str, Any]] = None # 有效阶梯计费配置(含继承)
|
||||
input_price_per_1m: float | None
|
||||
output_price_per_1m: float | None
|
||||
cache_creation_price_per_1m: float | None
|
||||
cache_read_price_per_1m: float | None
|
||||
cache_1h_creation_price_per_1m: float | None = None # 1h 缓存创建价格
|
||||
price_per_request: float | None = None # 按次计费价格
|
||||
effective_tiered_pricing: dict[str, Any] | None = None # 有效阶梯计费配置(含继承)
|
||||
tier_count: int = 1 # 阶梯数量
|
||||
supports_vision: Optional[bool] = None
|
||||
supports_function_calling: Optional[bool] = None
|
||||
supports_streaming: Optional[bool] = None
|
||||
supports_vision: bool | None = None
|
||||
supports_function_calling: bool | None = None
|
||||
supports_streaming: bool | None = None
|
||||
is_active: bool
|
||||
|
||||
|
||||
@@ -135,8 +136,8 @@ class ModelCatalogItem(BaseModel):
|
||||
|
||||
global_model_name: str # GlobalModel.name
|
||||
display_name: str # GlobalModel.display_name
|
||||
description: Optional[str] # GlobalModel.description
|
||||
providers: List[ModelCatalogProviderDetail] # 支持该模型的 Provider 列表
|
||||
description: str | None # GlobalModel.description
|
||||
providers: list[ModelCatalogProviderDetail] # 支持该模型的 Provider 列表
|
||||
price_range: ModelPriceRange # 价格区间(从所有 Provider 的 Model 中聚合)
|
||||
total_providers: int
|
||||
capabilities: ModelCapabilities # 能力聚合(从所有 Provider 的 Model 中聚合)
|
||||
@@ -145,18 +146,18 @@ class ModelCatalogItem(BaseModel):
|
||||
class ModelCatalogResponse(BaseModel):
|
||||
"""统一模型目录响应"""
|
||||
|
||||
models: List[ModelCatalogItem]
|
||||
models: list[ModelCatalogItem]
|
||||
total: int
|
||||
|
||||
|
||||
class ProviderModelPriceInfo(BaseModel):
|
||||
"""Provider 维度的模型价格信息"""
|
||||
|
||||
input_price_per_1m: Optional[float]
|
||||
output_price_per_1m: Optional[float]
|
||||
cache_creation_price_per_1m: Optional[float]
|
||||
cache_read_price_per_1m: Optional[float]
|
||||
price_per_request: Optional[float] = None # 按次计费价格
|
||||
input_price_per_1m: float | None
|
||||
output_price_per_1m: float | None
|
||||
cache_creation_price_per_1m: float | None
|
||||
cache_read_price_per_1m: float | None
|
||||
price_per_request: float | None = None # 按次计费价格
|
||||
|
||||
|
||||
class ProviderAvailableSourceModel(BaseModel):
|
||||
@@ -165,7 +166,7 @@ class ProviderAvailableSourceModel(BaseModel):
|
||||
global_model_name: str # GlobalModel.name
|
||||
display_name: str # GlobalModel.display_name
|
||||
provider_model_name: str # Model.provider_model_name (Provider 侧的模型名)
|
||||
model_id: Optional[str] # Model.id
|
||||
model_id: str | None # Model.id
|
||||
price: ProviderModelPriceInfo
|
||||
capabilities: ModelCapabilities
|
||||
is_active: bool
|
||||
@@ -174,7 +175,7 @@ class ProviderAvailableSourceModel(BaseModel):
|
||||
class ProviderAvailableSourceModelsResponse(BaseModel):
|
||||
"""Provider 可用统一模型响应"""
|
||||
|
||||
models: List[ProviderAvailableSourceModel]
|
||||
models: list[ProviderAvailableSourceModel]
|
||||
total: int
|
||||
|
||||
|
||||
@@ -187,41 +188,41 @@ class GlobalModelCreate(BaseModel):
|
||||
name: str = Field(..., min_length=1, max_length=100, description="统一模型名(唯一)")
|
||||
display_name: str = Field(..., min_length=1, max_length=100, description="显示名称")
|
||||
# 按次计费配置(可选,与阶梯计费叠加)
|
||||
default_price_per_request: Optional[float] = Field(None, ge=0, description="每次请求固定费用")
|
||||
default_price_per_request: float | None = Field(None, ge=0, description="每次请求固定费用")
|
||||
# 统一阶梯计费配置(必填)
|
||||
# 固定价格也用单阶梯表示: {"tiers": [{"up_to": null, "input_price_per_1m": X, ...}]}
|
||||
default_tiered_pricing: TieredPricingConfig = Field(
|
||||
..., description="阶梯计费配置(固定价格用单阶梯表示)"
|
||||
)
|
||||
# Key 能力配置 - 模型支持的能力列表(如 ["cache_1h", "context_1m"])
|
||||
supported_capabilities: Optional[List[str]] = Field(
|
||||
supported_capabilities: list[str] | None = Field(
|
||||
None, description="支持的 Key 能力列表"
|
||||
)
|
||||
# 模型配置(JSON格式)- 包含能力、规格、元信息等
|
||||
config: Optional[Dict[str, Any]] = Field(
|
||||
config: dict[str, Any] | None = Field(
|
||||
None,
|
||||
description="模型配置(streaming, vision, context_limit, description 等)"
|
||||
)
|
||||
is_active: Optional[bool] = Field(True, description="是否激活")
|
||||
is_active: bool | None = Field(True, description="是否激活")
|
||||
|
||||
|
||||
class GlobalModelUpdate(BaseModel):
|
||||
"""更新 GlobalModel 请求"""
|
||||
|
||||
display_name: Optional[str] = Field(None, min_length=1, max_length=100)
|
||||
is_active: Optional[bool] = None
|
||||
display_name: str | None = Field(None, min_length=1, max_length=100)
|
||||
is_active: bool | None = None
|
||||
# 按次计费配置
|
||||
default_price_per_request: Optional[float] = Field(None, ge=0, description="每次请求固定费用")
|
||||
default_price_per_request: float | None = Field(None, ge=0, description="每次请求固定费用")
|
||||
# 阶梯计费配置
|
||||
default_tiered_pricing: Optional[TieredPricingConfig] = Field(
|
||||
default_tiered_pricing: TieredPricingConfig | None = Field(
|
||||
None, description="阶梯计费配置"
|
||||
)
|
||||
# Key 能力配置 - 模型支持的能力列表(如 ["cache_1h", "context_1m"])
|
||||
supported_capabilities: Optional[List[str]] = Field(
|
||||
supported_capabilities: list[str] | None = Field(
|
||||
None, description="支持的 Key 能力列表"
|
||||
)
|
||||
# 模型配置(JSON格式)- 包含能力、规格、元信息等
|
||||
config: Optional[Dict[str, Any]] = Field(
|
||||
config: dict[str, Any] | None = Field(
|
||||
None,
|
||||
description="模型配置(streaming, vision, context_limit, description 等)"
|
||||
)
|
||||
@@ -235,25 +236,25 @@ class GlobalModelResponse(BaseModel):
|
||||
display_name: str
|
||||
is_active: bool
|
||||
# 按次计费配置
|
||||
default_price_per_request: Optional[float] = Field(None, description="每次请求固定费用")
|
||||
default_price_per_request: float | None = Field(None, description="每次请求固定费用")
|
||||
# 阶梯计费配置
|
||||
default_tiered_pricing: Optional[TieredPricingConfig] = Field(
|
||||
default_tiered_pricing: TieredPricingConfig | None = Field(
|
||||
default=None, description="阶梯计费配置"
|
||||
)
|
||||
# Key 能力配置 - 模型支持的能力列表
|
||||
supported_capabilities: Optional[List[str]] = Field(
|
||||
supported_capabilities: list[str] | None = Field(
|
||||
default=None, description="支持的 Key 能力列表"
|
||||
)
|
||||
# 模型配置(JSON格式)
|
||||
config: Optional[Dict[str, Any]] = Field(
|
||||
config: dict[str, Any] | None = Field(
|
||||
default=None,
|
||||
description="模型配置(streaming, vision, context_limit, description 等)"
|
||||
)
|
||||
# 统计数据(可选)
|
||||
provider_count: Optional[int] = Field(default=0, description="支持的 Provider 数量")
|
||||
usage_count: Optional[int] = Field(default=0, description="调用次数")
|
||||
provider_count: int | None = Field(default=0, description="支持的 Provider 数量")
|
||||
usage_count: int | None = Field(default=0, description="调用次数")
|
||||
created_at: datetime
|
||||
updated_at: Optional[datetime]
|
||||
updated_at: datetime | None
|
||||
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
@@ -269,54 +270,54 @@ class GlobalModelWithStats(GlobalModelResponse):
|
||||
class GlobalModelListResponse(BaseModel):
|
||||
"""GlobalModel 列表响应"""
|
||||
|
||||
models: List[GlobalModelResponse]
|
||||
models: list[GlobalModelResponse]
|
||||
total: int
|
||||
|
||||
|
||||
class GlobalModelProvidersResponse(BaseModel):
|
||||
"""GlobalModel 关联提供商列表响应"""
|
||||
|
||||
providers: List[ModelCatalogProviderDetail]
|
||||
providers: list[ModelCatalogProviderDetail]
|
||||
total: int
|
||||
|
||||
|
||||
class BatchAssignToProvidersRequest(BaseModel):
|
||||
"""批量为 Provider 添加 GlobalModel 实现"""
|
||||
|
||||
provider_ids: List[str] = Field(..., min_length=1, description="Provider ID 列表")
|
||||
provider_ids: list[str] = Field(..., min_length=1, description="Provider ID 列表")
|
||||
create_models: bool = Field(default=False, description="是否自动创建 Model 记录")
|
||||
|
||||
|
||||
class BatchAssignToProvidersResponse(BaseModel):
|
||||
"""批量分配响应"""
|
||||
|
||||
success: List[dict]
|
||||
errors: List[dict]
|
||||
success: list[dict]
|
||||
errors: list[dict]
|
||||
|
||||
|
||||
class BatchAssignModelsToProviderRequest(BaseModel):
|
||||
"""批量为 Provider 关联 GlobalModel"""
|
||||
|
||||
global_model_ids: List[str] = Field(..., min_length=1, description="GlobalModel ID 列表")
|
||||
global_model_ids: list[str] = Field(..., min_length=1, description="GlobalModel ID 列表")
|
||||
|
||||
|
||||
class BatchAssignModelsToProviderResponse(BaseModel):
|
||||
"""批量关联 GlobalModel 到 Provider 的响应"""
|
||||
|
||||
success: List[dict]
|
||||
errors: List[dict]
|
||||
success: list[dict]
|
||||
errors: list[dict]
|
||||
|
||||
|
||||
class ImportFromUpstreamRequest(BaseModel):
|
||||
"""从上游提供商导入模型请求"""
|
||||
|
||||
model_ids: List[str] = Field(..., min_length=1, description="上游模型 ID 列表")
|
||||
model_ids: list[str] = Field(..., min_length=1, description="上游模型 ID 列表")
|
||||
# 价格覆盖配置(应用于所有导入的模型)
|
||||
tiered_pricing: Optional[Dict] = Field(
|
||||
tiered_pricing: dict | None = Field(
|
||||
None,
|
||||
description="阶梯计费配置(可选),格式: {tiers: [{up_to, input_price_per_1m, output_price_per_1m, ...}]}"
|
||||
)
|
||||
price_per_request: Optional[float] = Field(
|
||||
price_per_request: float | None = Field(
|
||||
None,
|
||||
ge=0,
|
||||
description="按次计费价格(可选,单位:美元)"
|
||||
@@ -328,8 +329,8 @@ class ImportFromUpstreamSuccessItem(BaseModel):
|
||||
|
||||
model_id: str = Field(..., description="上游模型 ID")
|
||||
provider_model_id: str = Field(..., description="Provider Model ID")
|
||||
global_model_id: Optional[str] = Field("", description="GlobalModel ID(如果已关联)")
|
||||
global_model_name: Optional[str] = Field("", description="GlobalModel 名称(如果已关联)")
|
||||
global_model_id: str | None = Field("", description="GlobalModel ID(如果已关联)")
|
||||
global_model_name: str | None = Field("", description="GlobalModel 名称(如果已关联)")
|
||||
created_global_model: bool = Field(False, description="是否新创建了 GlobalModel(始终为 false)")
|
||||
|
||||
|
||||
@@ -343,8 +344,8 @@ class ImportFromUpstreamErrorItem(BaseModel):
|
||||
class ImportFromUpstreamResponse(BaseModel):
|
||||
"""从上游提供商导入模型响应"""
|
||||
|
||||
success: List[ImportFromUpstreamSuccessItem]
|
||||
errors: List[ImportFromUpstreamErrorItem]
|
||||
success: list[ImportFromUpstreamSuccessItem]
|
||||
errors: list[ImportFromUpstreamErrorItem]
|
||||
|
||||
|
||||
__all__ = [
|
||||
|
||||
Reference in New Issue
Block a user