Files
Aether/src/models/pydantic_models.py

342 lines
12 KiB
Python
Raw Normal View History

2025-12-10 20:52:44 +08:00
"""
Pydantic 数据模型阶段一统一模型管理
"""
from datetime import datetime
from typing import Any, Dict, List, Optional
from pydantic import BaseModel, Field, model_validator
# ========== 阶梯计费相关模型 ==========
class CacheTTLPricing(BaseModel):
"""缓存时长定价配置"""
ttl_minutes: int = Field(..., ge=1, description="缓存时长(分钟)")
cache_creation_price_per_1m: float = Field(..., ge=0, description="该时长的缓存创建价格/M tokens")
class PricingTier(BaseModel):
"""单个价格阶梯配置"""
up_to: Optional[int] = Field(
None,
ge=1,
description="阶梯上限tokensnull 表示无上限(最后一个阶梯)"
)
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(
None, ge=0, description="缓存创建价格/M tokens"
)
cache_read_price_per_1m: Optional[float] = Field(
None, ge=0, description="缓存读取价格/M tokens"
)
cache_ttl_pricing: Optional[List[CacheTTLPricing]] = Field(
None, description="按缓存时长分价格(可选)"
)
class TieredPricingConfig(BaseModel):
"""阶梯计费配置"""
tiers: List[PricingTier] = Field(
...,
min_length=1,
description="价格阶梯列表,按 up_to 升序排列"
)
@model_validator(mode="after")
def validate_tiers(self) -> "TieredPricingConfig":
"""验证阶梯配置的合法性"""
tiers = self.tiers
if not tiers:
raise ValueError("至少需要一个价格阶梯")
# 检查阶梯顺序和唯一性
prev_up_to = 0
has_unlimited = False
for i, tier in enumerate(tiers):
if has_unlimited:
raise ValueError("无上限阶梯up_to=null必须是最后一个")
if tier.up_to is None:
has_unlimited = True
else:
if tier.up_to <= prev_up_to:
raise ValueError(
f"阶梯 {i+1} 的 up_to ({tier.up_to}) 必须大于前一个阶梯 ({prev_up_to})"
)
prev_up_to = tier.up_to
# 验证缓存时长定价顺序
if tier.cache_ttl_pricing:
prev_ttl = 0
for ttl_pricing in tier.cache_ttl_pricing:
if ttl_pricing.ttl_minutes <= prev_ttl:
raise ValueError(
f"cache_ttl_pricing 必须按 ttl_minutes 升序排列"
)
prev_ttl = ttl_pricing.ttl_minutes
# 最后一个阶梯必须是无上限的
if not has_unlimited:
raise ValueError("最后一个阶梯必须设置 up_to=null无上限")
return self
# ========== 其他模型 ==========
class ModelCapabilities(BaseModel):
"""模型能力聚合"""
supports_vision: bool = False
supports_function_calling: bool = False
supports_streaming: bool = False
class ModelPriceRange(BaseModel):
"""统一模型价格区间"""
min_input: Optional[float] = None
max_input: Optional[float] = None
min_output: Optional[float] = None
max_output: Optional[float] = None
class ModelCatalogProviderDetail(BaseModel):
"""统一模型目录中的关联提供商信息"""
provider_id: str
provider_name: str
provider_display_name: Optional[str]
model_id: Optional[str]
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 # 有效阶梯计费配置(含继承)
tier_count: int = 1 # 阶梯数量
supports_vision: Optional[bool] = None
supports_function_calling: Optional[bool] = None
supports_streaming: Optional[bool] = None
is_active: bool
class ModelCatalogItem(BaseModel):
"""统一模型目录条目(基于 GlobalModel"""
2025-12-10 20:52:44 +08:00
global_model_name: str # GlobalModel.name
display_name: str # GlobalModel.display_name
description: Optional[str] # GlobalModel.description
providers: List[ModelCatalogProviderDetail] # 支持该模型的 Provider 列表
price_range: ModelPriceRange # 价格区间(从所有 Provider 的 Model 中聚合)
total_providers: int
capabilities: ModelCapabilities # 能力聚合(从所有 Provider 的 Model 中聚合)
class ModelCatalogResponse(BaseModel):
"""统一模型目录响应"""
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 # 按次计费价格
class ProviderAvailableSourceModel(BaseModel):
"""Provider 支持的统一模型条目"""
2025-12-10 20:52:44 +08:00
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
price: ProviderModelPriceInfo
capabilities: ModelCapabilities
is_active: bool
class ProviderAvailableSourceModelsResponse(BaseModel):
"""Provider 可用统一模型响应"""
models: List[ProviderAvailableSourceModel]
total: int
# ========== GlobalModel 相关模型 ==========
2025-12-10 20:52:44 +08:00
class GlobalModelCreate(BaseModel):
"""创建 GlobalModel 请求"""
name: str = Field(..., min_length=1, max_length=100, description="统一模型名(唯一)")
display_name: str = Field(..., min_length=1, max_length=100, description="显示名称")
description: Optional[str] = Field(None, description="模型描述")
official_url: Optional[str] = Field(None, max_length=500, description="官方文档链接")
icon_url: Optional[str] = Field(None, max_length=500, description="图标 URL")
# 按次计费配置(可选,与阶梯计费叠加)
default_price_per_request: Optional[float] = Field(None, ge=0, description="每次请求固定费用")
# 统一阶梯计费配置(必填)
# 固定价格也用单阶梯表示: {"tiers": [{"up_to": null, "input_price_per_1m": X, ...}]}
default_tiered_pricing: TieredPricingConfig = Field(
..., description="阶梯计费配置(固定价格用单阶梯表示)"
)
# 默认能力配置
default_supports_vision: Optional[bool] = Field(False, description="默认是否支持视觉")
default_supports_function_calling: Optional[bool] = Field(
False, description="默认是否支持函数调用"
)
default_supports_streaming: Optional[bool] = Field(True, description="默认是否支持流式输出")
default_supports_extended_thinking: Optional[bool] = Field(
False, description="默认是否支持扩展思考"
)
default_supports_image_generation: Optional[bool] = Field(
False, description="默认是否支持图像生成"
)
# Key 能力配置 - 模型支持的能力列表(如 ["cache_1h", "context_1m"]
supported_capabilities: Optional[List[str]] = Field(
None, description="支持的 Key 能力列表"
)
is_active: Optional[bool] = Field(True, description="是否激活")
class GlobalModelUpdate(BaseModel):
"""更新 GlobalModel 请求"""
display_name: Optional[str] = Field(None, min_length=1, max_length=100)
description: Optional[str] = None
official_url: Optional[str] = Field(None, max_length=500)
icon_url: Optional[str] = Field(None, max_length=500)
is_active: Optional[bool] = None
# 按次计费配置
default_price_per_request: Optional[float] = Field(None, ge=0, description="每次请求固定费用")
# 阶梯计费配置
default_tiered_pricing: Optional[TieredPricingConfig] = Field(
None, description="阶梯计费配置"
)
# 默认能力配置
default_supports_vision: Optional[bool] = None
default_supports_function_calling: Optional[bool] = None
default_supports_streaming: Optional[bool] = None
default_supports_extended_thinking: Optional[bool] = None
default_supports_image_generation: Optional[bool] = None
# Key 能力配置 - 模型支持的能力列表(如 ["cache_1h", "context_1m"]
supported_capabilities: Optional[List[str]] = Field(
None, description="支持的 Key 能力列表"
)
class GlobalModelResponse(BaseModel):
"""GlobalModel 响应"""
id: str
name: str
display_name: str
description: Optional[str]
official_url: Optional[str]
icon_url: Optional[str]
is_active: bool
# 按次计费配置
default_price_per_request: Optional[float] = Field(None, description="每次请求固定费用")
# 阶梯计费配置
default_tiered_pricing: TieredPricingConfig = Field(
..., description="阶梯计费配置"
)
# 默认能力配置
default_supports_vision: Optional[bool]
default_supports_function_calling: Optional[bool]
default_supports_streaming: Optional[bool]
default_supports_extended_thinking: Optional[bool]
default_supports_image_generation: Optional[bool]
# Key 能力配置 - 模型支持的能力列表
supported_capabilities: Optional[List[str]] = Field(
default=None, description="支持的 Key 能力列表"
)
# 统计数据(可选)
provider_count: Optional[int] = Field(default=0, description="支持的 Provider 数量")
usage_count: Optional[int] = Field(default=0, description="调用次数")
created_at: datetime
updated_at: Optional[datetime]
class Config:
from_attributes = True
class GlobalModelWithStats(GlobalModelResponse):
"""带统计信息的 GlobalModel"""
total_models: int = Field(..., description="关联的 Model 数量")
total_providers: int = Field(..., description="支持的 Provider 数量")
price_range: ModelPriceRange
class GlobalModelListResponse(BaseModel):
"""GlobalModel 列表响应"""
models: List[GlobalModelResponse]
total: int
class BatchAssignToProvidersRequest(BaseModel):
"""批量为 Provider 添加 GlobalModel 实现"""
provider_ids: List[str] = Field(..., min_length=1, description="Provider ID 列表")
2025-12-10 20:52:44 +08:00
create_models: bool = Field(default=False, description="是否自动创建 Model 记录")
class BatchAssignToProvidersResponse(BaseModel):
"""批量分配响应"""
success: List[dict]
errors: List[dict]
class BatchAssignModelsToProviderRequest(BaseModel):
"""批量为 Provider 关联 GlobalModel"""
global_model_ids: List[str] = Field(..., min_length=1, description="GlobalModel ID 列表")
class BatchAssignModelsToProviderResponse(BaseModel):
"""批量关联 GlobalModel 到 Provider 的响应"""
success: List[dict]
errors: List[dict]
__all__ = [
"BatchAssignModelsToProviderRequest",
"BatchAssignModelsToProviderResponse",
"BatchAssignToProvidersRequest",
"BatchAssignToProvidersResponse",
"GlobalModelCreate",
"GlobalModelListResponse",
"GlobalModelResponse",
"GlobalModelUpdate",
"GlobalModelWithStats",
"ModelCapabilities",
"ModelCatalogItem",
"ModelCatalogProviderDetail",
"ModelCatalogResponse",
"ModelPriceRange",
"ProviderAvailableSourceModel",
"ProviderAvailableSourceModelsResponse",
"ProviderModelPriceInfo",
]