mirror of
https://github.com/fawney19/Aether.git
synced 2026-09-03 09:50:21 +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:
@@ -8,16 +8,15 @@
|
||||
- 自定义计费维度
|
||||
"""
|
||||
|
||||
from typing import Any, Dict, List, Optional, Tuple
|
||||
from __future__ import annotations
|
||||
from typing import Any
|
||||
|
||||
from src.services.billing.models import (
|
||||
BillingDimension,
|
||||
BillingUnit,
|
||||
CostBreakdown,
|
||||
StandardizedUsage,
|
||||
)
|
||||
from src.services.billing.templates import (
|
||||
BILLING_TEMPLATE_REGISTRY,
|
||||
BillingTemplates,
|
||||
get_template,
|
||||
)
|
||||
@@ -50,8 +49,8 @@ class BillingCalculator:
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
dimensions: Optional[List[BillingDimension]] = None,
|
||||
template: Optional[str] = None,
|
||||
dimensions: list[BillingDimension] | None = None,
|
||||
template: str | None = None,
|
||||
):
|
||||
"""
|
||||
初始化计费计算器
|
||||
@@ -73,10 +72,10 @@ class BillingCalculator:
|
||||
def calculate(
|
||||
self,
|
||||
usage: StandardizedUsage,
|
||||
prices: Dict[str, float],
|
||||
tiered_pricing: Optional[Dict[str, Any]] = None,
|
||||
cache_ttl_minutes: Optional[int] = None,
|
||||
total_input_context: Optional[int] = None,
|
||||
prices: dict[str, float],
|
||||
tiered_pricing: dict[str, Any] | None = None,
|
||||
cache_ttl_minutes: int | None = None,
|
||||
total_input_context: int | None = None,
|
||||
) -> CostBreakdown:
|
||||
"""
|
||||
计算费用
|
||||
@@ -131,9 +130,9 @@ class BillingCalculator:
|
||||
def _get_tier(
|
||||
self,
|
||||
usage: StandardizedUsage,
|
||||
tiered_pricing: Dict[str, Any],
|
||||
total_input_context: Optional[int] = None,
|
||||
) -> Tuple[Optional[Dict[str, Any]], Optional[int]]:
|
||||
tiered_pricing: dict[str, Any],
|
||||
total_input_context: int | None = None,
|
||||
) -> tuple[dict[str, Any] | None, int | None]:
|
||||
"""
|
||||
确定价格阶梯
|
||||
|
||||
@@ -178,9 +177,9 @@ class BillingCalculator:
|
||||
|
||||
def _get_cache_read_price_for_ttl(
|
||||
self,
|
||||
tier: Dict[str, Any],
|
||||
tier: dict[str, Any],
|
||||
cache_ttl_minutes: int,
|
||||
) -> Optional[float]:
|
||||
) -> float | None:
|
||||
"""
|
||||
根据缓存 TTL 获取缓存读取价格
|
||||
|
||||
@@ -212,7 +211,7 @@ class BillingCalculator:
|
||||
return None
|
||||
|
||||
@classmethod
|
||||
def from_config(cls, config: Dict[str, Any]) -> "BillingCalculator":
|
||||
def from_config(cls, config: dict[str, Any]) -> BillingCalculator:
|
||||
"""
|
||||
从配置创建计费计算器
|
||||
|
||||
@@ -238,15 +237,15 @@ class BillingCalculator:
|
||||
|
||||
return cls(template=config.get("template", "claude"))
|
||||
|
||||
def get_dimension_names(self) -> List[str]:
|
||||
def get_dimension_names(self) -> list[str]:
|
||||
"""获取所有计费维度名称"""
|
||||
return [dim.name for dim in self.dimensions]
|
||||
|
||||
def get_required_price_fields(self) -> List[str]:
|
||||
def get_required_price_fields(self) -> list[str]:
|
||||
"""获取所需的价格字段名称"""
|
||||
return [dim.price_field for dim in self.dimensions]
|
||||
|
||||
def get_required_usage_fields(self) -> List[str]:
|
||||
def get_required_usage_fields(self) -> list[str]:
|
||||
"""获取所需的 usage 字段名称"""
|
||||
return [dim.usage_field for dim in self.dimensions]
|
||||
|
||||
@@ -258,14 +257,14 @@ def calculate_request_cost(
|
||||
cache_read_input_tokens: int,
|
||||
input_price_per_1m: float,
|
||||
output_price_per_1m: float,
|
||||
cache_creation_price_per_1m: Optional[float],
|
||||
cache_read_price_per_1m: Optional[float],
|
||||
price_per_request: Optional[float],
|
||||
tiered_pricing: Optional[Dict[str, Any]] = None,
|
||||
cache_ttl_minutes: Optional[int] = None,
|
||||
total_input_context: Optional[int] = None,
|
||||
cache_creation_price_per_1m: float | None,
|
||||
cache_read_price_per_1m: float | None,
|
||||
price_per_request: float | None,
|
||||
tiered_pricing: dict[str, Any] | None = None,
|
||||
cache_ttl_minutes: int | None = None,
|
||||
total_input_context: int | None = None,
|
||||
billing_template: str = "claude",
|
||||
) -> Dict[str, Any]:
|
||||
) -> dict[str, Any]:
|
||||
"""
|
||||
计算请求成本的便捷函数
|
||||
|
||||
@@ -309,7 +308,7 @@ def calculate_request_cost(
|
||||
)
|
||||
|
||||
# 构建价格配置
|
||||
prices: Dict[str, float] = {
|
||||
prices: dict[str, float] = {
|
||||
"input_price_per_1m": input_price_per_1m,
|
||||
"output_price_per_1m": output_price_per_1m,
|
||||
}
|
||||
|
||||
@@ -8,9 +8,10 @@
|
||||
- CostBreakdown: 计费明细结果
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
from dataclasses import dataclass, field
|
||||
from enum import Enum
|
||||
from typing import Any, Dict, Optional
|
||||
from typing import Any
|
||||
|
||||
|
||||
class BillingUnit(str, Enum):
|
||||
@@ -66,7 +67,7 @@ class BillingDimension:
|
||||
|
||||
return 0.0
|
||||
|
||||
def to_dict(self) -> Dict[str, Any]:
|
||||
def to_dict(self) -> dict[str, Any]:
|
||||
"""转换为字典(用于序列化)"""
|
||||
return {
|
||||
"name": self.name,
|
||||
@@ -77,7 +78,7 @@ class BillingDimension:
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def from_dict(cls, data: Dict[str, Any]) -> "BillingDimension":
|
||||
def from_dict(cls, data: dict[str, Any]) -> BillingDimension:
|
||||
"""从字典创建实例"""
|
||||
return cls(
|
||||
name=data["name"],
|
||||
@@ -114,7 +115,7 @@ class StandardizedUsage:
|
||||
request_count: int = 1
|
||||
|
||||
# 扩展字段(未来可能需要的额外维度)
|
||||
extra: Dict[str, Any] = field(default_factory=dict)
|
||||
extra: dict[str, Any] = field(default_factory=dict)
|
||||
|
||||
def get(self, field_name: str, default: Any = 0) -> Any:
|
||||
"""
|
||||
@@ -149,9 +150,9 @@ class StandardizedUsage:
|
||||
else:
|
||||
self.extra[field_name] = value
|
||||
|
||||
def to_dict(self) -> Dict[str, Any]:
|
||||
def to_dict(self) -> dict[str, Any]:
|
||||
"""转换为字典"""
|
||||
result: Dict[str, Any] = {
|
||||
result: dict[str, Any] = {
|
||||
"input_tokens": self.input_tokens,
|
||||
"output_tokens": self.output_tokens,
|
||||
"cache_creation_tokens": self.cache_creation_tokens,
|
||||
@@ -165,7 +166,7 @@ class StandardizedUsage:
|
||||
return result
|
||||
|
||||
@classmethod
|
||||
def from_dict(cls, data: Dict[str, Any]) -> "StandardizedUsage":
|
||||
def from_dict(cls, data: dict[str, Any]) -> StandardizedUsage:
|
||||
"""从字典创建实例"""
|
||||
extra = data.pop("extra", {}) if "extra" in data else {}
|
||||
# 只取已知字段
|
||||
@@ -191,19 +192,19 @@ class CostBreakdown:
|
||||
"""
|
||||
|
||||
# 各维度费用 {"input": 0.01, "output": 0.02, "cache_read": 0.001, ...}
|
||||
costs: Dict[str, float] = field(default_factory=dict)
|
||||
costs: dict[str, float] = field(default_factory=dict)
|
||||
|
||||
# 总费用
|
||||
total_cost: float = 0.0
|
||||
|
||||
# 命中的阶梯索引(如果使用阶梯计费)
|
||||
tier_index: Optional[int] = None
|
||||
tier_index: int | None = None
|
||||
|
||||
# 货币单位
|
||||
currency: str = "USD"
|
||||
|
||||
# 使用的价格(用于记录和审计)
|
||||
effective_prices: Dict[str, float] = field(default_factory=dict)
|
||||
effective_prices: dict[str, float] = field(default_factory=dict)
|
||||
|
||||
# =========================================================================
|
||||
# 兼容旧接口的属性(便于渐进式迁移)
|
||||
@@ -244,7 +245,7 @@ class CostBreakdown:
|
||||
"""缓存存储费用(豆包等)"""
|
||||
return self.costs.get("cache_storage", 0.0)
|
||||
|
||||
def to_dict(self) -> Dict[str, Any]:
|
||||
def to_dict(self) -> dict[str, Any]:
|
||||
"""转换为字典"""
|
||||
return {
|
||||
"costs": self.costs,
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
- PER_REQUEST: 按次计费
|
||||
"""
|
||||
|
||||
from typing import Dict, List, Optional
|
||||
|
||||
from src.services.billing.models import BillingDimension, BillingUnit
|
||||
|
||||
@@ -25,7 +24,7 @@ class BillingTemplates:
|
||||
# - 缓存读取(约 0.1x 输入价格)
|
||||
# - 按次计费(可选,配置 price_per_request 时生效)
|
||||
# =========================================================================
|
||||
CLAUDE_STANDARD: List[BillingDimension] = [
|
||||
CLAUDE_STANDARD: list[BillingDimension] = [
|
||||
BillingDimension(
|
||||
name="input",
|
||||
usage_field="input_tokens",
|
||||
@@ -61,7 +60,7 @@ class BillingTemplates:
|
||||
# - 缓存读取(部分模型支持,无缓存创建费用)
|
||||
# - 按次计费(可选,配置 price_per_request 时生效)
|
||||
# =========================================================================
|
||||
OPENAI_STANDARD: List[BillingDimension] = [
|
||||
OPENAI_STANDARD: list[BillingDimension] = [
|
||||
BillingDimension(
|
||||
name="input",
|
||||
usage_field="input_tokens",
|
||||
@@ -95,7 +94,7 @@ class BillingTemplates:
|
||||
#
|
||||
# 注意:豆包的缓存创建是免费的,但存储需要按时付费
|
||||
# =========================================================================
|
||||
DOUBAO_STANDARD: List[BillingDimension] = [
|
||||
DOUBAO_STANDARD: list[BillingDimension] = [
|
||||
BillingDimension(
|
||||
name="input",
|
||||
usage_field="input_tokens",
|
||||
@@ -132,7 +131,7 @@ class BillingTemplates:
|
||||
# - 缓存读取
|
||||
# - 按次计费(用于图片生成等模型,需配置 price_per_request)
|
||||
# =========================================================================
|
||||
GEMINI_STANDARD: List[BillingDimension] = [
|
||||
GEMINI_STANDARD: list[BillingDimension] = [
|
||||
BillingDimension(
|
||||
name="input",
|
||||
usage_field="input_tokens",
|
||||
@@ -161,7 +160,7 @@ class BillingTemplates:
|
||||
# - 适用于某些图片生成模型、特殊 API 等
|
||||
# - 仅按请求次数计费,不按 token 计费
|
||||
# =========================================================================
|
||||
PER_REQUEST: List[BillingDimension] = [
|
||||
PER_REQUEST: list[BillingDimension] = [
|
||||
BillingDimension(
|
||||
name="request",
|
||||
usage_field="request_count",
|
||||
@@ -174,7 +173,7 @@ class BillingTemplates:
|
||||
# 混合计费(按次 + 按 token)
|
||||
# - 某些模型既有固定费用又有 token 费用
|
||||
# =========================================================================
|
||||
HYBRID_STANDARD: List[BillingDimension] = [
|
||||
HYBRID_STANDARD: list[BillingDimension] = [
|
||||
BillingDimension(
|
||||
name="input",
|
||||
usage_field="input_tokens",
|
||||
@@ -198,7 +197,7 @@ class BillingTemplates:
|
||||
# 模板注册表
|
||||
# =========================================================================
|
||||
|
||||
BILLING_TEMPLATE_REGISTRY: Dict[str, List[BillingDimension]] = {
|
||||
BILLING_TEMPLATE_REGISTRY: dict[str, list[BillingDimension]] = {
|
||||
# 按厂商名称
|
||||
"claude": BillingTemplates.CLAUDE_STANDARD,
|
||||
"anthropic": BillingTemplates.CLAUDE_STANDARD,
|
||||
@@ -215,7 +214,7 @@ BILLING_TEMPLATE_REGISTRY: Dict[str, List[BillingDimension]] = {
|
||||
}
|
||||
|
||||
|
||||
def get_template(name: Optional[str]) -> List[BillingDimension]:
|
||||
def get_template(name: str | None) -> list[BillingDimension]:
|
||||
"""
|
||||
获取计费模板
|
||||
|
||||
@@ -236,6 +235,6 @@ def get_template(name: Optional[str]) -> List[BillingDimension]:
|
||||
return template
|
||||
|
||||
|
||||
def list_templates() -> List[str]:
|
||||
def list_templates() -> list[str]:
|
||||
"""列出所有可用的模板名称"""
|
||||
return list(BILLING_TEMPLATE_REGISTRY.keys())
|
||||
|
||||
@@ -9,7 +9,7 @@ Usage 字段映射器
|
||||
- GEMINI / GEMINI_CLI: Google Gemini API
|
||||
"""
|
||||
|
||||
from typing import Any, Dict, Optional
|
||||
from typing import Any
|
||||
|
||||
from src.services.billing.models import StandardizedUsage
|
||||
|
||||
@@ -47,7 +47,7 @@ class UsageMapper:
|
||||
# =========================================================================
|
||||
|
||||
# OpenAI 格式字段映射
|
||||
OPENAI_MAPPING: Dict[str, str] = {
|
||||
OPENAI_MAPPING: dict[str, str] = {
|
||||
"prompt_tokens": "input_tokens",
|
||||
"completion_tokens": "output_tokens",
|
||||
"prompt_tokens_details.cached_tokens": "cache_read_tokens",
|
||||
@@ -55,7 +55,7 @@ class UsageMapper:
|
||||
}
|
||||
|
||||
# Claude 格式字段映射
|
||||
CLAUDE_MAPPING: Dict[str, str] = {
|
||||
CLAUDE_MAPPING: dict[str, str] = {
|
||||
"input_tokens": "input_tokens",
|
||||
"output_tokens": "output_tokens",
|
||||
"cache_creation_input_tokens": "cache_creation_tokens",
|
||||
@@ -63,7 +63,7 @@ class UsageMapper:
|
||||
}
|
||||
|
||||
# Gemini 格式字段映射
|
||||
GEMINI_MAPPING: Dict[str, str] = {
|
||||
GEMINI_MAPPING: dict[str, str] = {
|
||||
"promptTokenCount": "input_tokens",
|
||||
"candidatesTokenCount": "output_tokens",
|
||||
"cachedContentTokenCount": "cache_read_tokens",
|
||||
@@ -74,7 +74,7 @@ class UsageMapper:
|
||||
}
|
||||
|
||||
# 格式名称到映射的对应关系
|
||||
FORMAT_MAPPINGS: Dict[str, Dict[str, str]] = {
|
||||
FORMAT_MAPPINGS: dict[str, dict[str, str]] = {
|
||||
"OPENAI": OPENAI_MAPPING,
|
||||
"OPENAI_CLI": OPENAI_MAPPING,
|
||||
"CLAUDE": CLAUDE_MAPPING,
|
||||
@@ -86,9 +86,9 @@ class UsageMapper:
|
||||
@classmethod
|
||||
def map(
|
||||
cls,
|
||||
raw_usage: Dict[str, Any],
|
||||
raw_usage: dict[str, Any],
|
||||
api_format: str,
|
||||
extra_mapping: Optional[Dict[str, str]] = None,
|
||||
extra_mapping: dict[str, str] | None = None,
|
||||
) -> StandardizedUsage:
|
||||
"""
|
||||
将原始 usage 映射为标准化格式
|
||||
@@ -124,7 +124,7 @@ class UsageMapper:
|
||||
@classmethod
|
||||
def map_from_response(
|
||||
cls,
|
||||
response: Dict[str, Any],
|
||||
response: dict[str, Any],
|
||||
api_format: str,
|
||||
) -> StandardizedUsage:
|
||||
"""
|
||||
@@ -145,7 +145,7 @@ class UsageMapper:
|
||||
format_upper = api_format.upper() if api_format else ""
|
||||
|
||||
# 提取 usage 部分
|
||||
usage_data: Dict[str, Any] = {}
|
||||
usage_data: dict[str, Any] = {}
|
||||
|
||||
if format_upper.startswith("GEMINI"):
|
||||
# Gemini: usageMetadata
|
||||
@@ -162,7 +162,7 @@ class UsageMapper:
|
||||
return cls.map(usage_data, api_format)
|
||||
|
||||
@classmethod
|
||||
def _get_mapping(cls, api_format: str) -> Dict[str, str]:
|
||||
def _get_mapping(cls, api_format: str) -> dict[str, str]:
|
||||
"""获取对应格式的字段映射"""
|
||||
if not api_format:
|
||||
return cls.CLAUDE_MAPPING
|
||||
@@ -182,7 +182,7 @@ class UsageMapper:
|
||||
return cls.CLAUDE_MAPPING
|
||||
|
||||
@classmethod
|
||||
def _get_nested_value(cls, data: Dict[str, Any], path: str) -> Any:
|
||||
def _get_nested_value(cls, data: dict[str, Any], path: str) -> Any:
|
||||
"""
|
||||
获取嵌套字段值
|
||||
|
||||
@@ -212,7 +212,7 @@ class UsageMapper:
|
||||
return value
|
||||
|
||||
@classmethod
|
||||
def register_format(cls, format_name: str, mapping: Dict[str, str]) -> None:
|
||||
def register_format(cls, format_name: str, mapping: dict[str, str]) -> None:
|
||||
"""
|
||||
注册新的格式映射
|
||||
|
||||
@@ -234,7 +234,7 @@ class UsageMapper:
|
||||
|
||||
|
||||
def map_usage(
|
||||
raw_usage: Dict[str, Any],
|
||||
raw_usage: dict[str, Any],
|
||||
api_format: str,
|
||||
) -> StandardizedUsage:
|
||||
"""
|
||||
@@ -251,7 +251,7 @@ def map_usage(
|
||||
|
||||
|
||||
def map_usage_from_response(
|
||||
response: Dict[str, Any],
|
||||
response: dict[str, Any],
|
||||
api_format: str,
|
||||
) -> StandardizedUsage:
|
||||
"""
|
||||
|
||||
Reference in New Issue
Block a user